Полка с подсветкой в шкафу: сверлить корпус не хотелось, и кнопкой стал кусок провода, приклеенный изнутри к декоративной панели — тач-пин ESP32 чувствует касание любого проводка. Первую неделю всё сияло, потом подсветка начала включаться сама, чаще ночью: провод длиной в сорок сантиметров работал антенной, а порог я выставил «на глазок», скопировав цифру из чужого скетча. Понадобился вечер с Serial-журналом, чтобы понять: у ёмкостного входа нет универсального порога — есть покой конкретной платы с конкретным проводом, и от него считается всё остальное.
У исходного ESP32 таких входов десять: T0-T9, каждый посажен на свой GPIO, и любой из них превращает в кнопку гвоздь, фольгу, дверную ручку или мокрый листик растения. Здесь разберём, что именно показывает touchRead, как поставить порог, который не придётся перепрошивать, и почему самая частая жалоба — фантомные касания — лечится гистерезисом и подтверждением, а не молитвой. Пробуждение из глубокого сна по касанию — тема отдельная, я разбирал её рядом с кнопкой и причинами пробуждения; этот урок — про бодрствующую плату.
Что показывает touchRead и почему падает от пальца
Механика честная и нехитрая: схема платы в окне измерения считает циклы заряда-разряда ёмкости входа — touchRead возвращает именно это число. Палец подносится к проводку — добавляется ёмкость тела, заряжать становится дольше, циклов укладывается меньше, значение падает. Порядок цифр на моих двух платах DevKit: в покое порядка сотни (у одной 118, у другой 94), при касании за изоляцию провода — падение на треть-половину, голым пальцем по оголённому контакту — до трети покоя. Цифры зависят от платы, провода, толщины краски и ваших рук, поэтому всерьёз их воспринимать можно только у себя на стенде.
Отсюда первый рабочий вывод: калибровка делается на месте, а не берётся из форума. В setup печатаем среднее из восьми чтений покоя, ставим порог на уровне примерно шестидесяти процентов от него и проверяем касанием. Второй вывод: значение живёт, пока есть чем измерять — окно измерения занимает десятки микросекунд, и опрос хоть каждый проход loop не потревожит никого; каркас опроса без delay собирайте по уроку про millis вместо delay. А ниже — карта десяти входов с теми сюрпризами, которые я собрал на собственные шишки.
| Пин | GPIO | Что учесть |
|---|---|---|
| T0 | GPIO4 | спокойный вход рядом с краю платы — первым пробую на новом стенде |
| T1 | GPIO0 | на DevKit это кнопка BOOT: нажатие штатной кнопки читается как касание |
| T2, T5 | GPIO2, GPIO12 | strap-пины: их уровень при старте решает, как грузится плата — проверяйте по распиновке |
| T8, T9 | GPIO33, GPIO32 | тихая пара у края платы — мои любимые для навесного проводка |
const int TACH = 4; // T0 = GPIO4: проводок-кнопка на пинconst int LED = 2; // подсветка полки (на GPIO2 у меня она штатная) int pokoj = 0; // уровень покоя по калибровкеint porog = 0; // порог касания: 60% покояint otpusk = 0; // порог отпускания: гистерезисbool svet = false;int podtverzhdenij = 0; // сколько замеров ниже порога подрядunsigned long tLast = 0; void kalibrovka() { long sum = 0; for (int i = 0; i < 8; i++) { sum += touchRead(TACH); delay(20); } pokoj = sum / 8; porog = pokoj * 60 / 100; otpusk = porog + 12; // гистерезис: отпускание выше срабатывания Serial.print("pokoj="); Serial.print(pokoj); Serial.print(" porog="); Serial.println(porog);} void setup() { Serial.begin(115200); pinMode(LED, OUTPUT); kalibrovka();} void loop() { unsigned long ms = millis(); if (ms - tLast < 40) return; // замеры раз в 40 мс tLast = ms; int v = touchRead(TACH); if (!svet && v < porog) { podtverzhdenij++; if (podtverzhdenij >= 2) { // два замера подряд — касание честное svet = true; digitalWrite(LED, HIGH); Serial.println("kasanie prinyato"); } } else if (svet && v > otpusk) { svet = false; podtverzhdenij = 0; digitalWrite(LED, LOW); } else if (!svet) { podtverzhdenij = 0; }}Три приёма в скетче несут всю нагрузку. Гистерезис — порог отпускания выше порога срабатывания — не даёт свету мигать на переходной кромке, когда палец дрожит на границе. Подтверждение двумя замерами подряд отсекает одиночные провалы от наводок: у механической кнопки для той же цели есть целая копилка способов, собранная в уроке про четыре способа антидребезга — у ёмкостного входа физика другая, а идея та же. Опрос с шагом сорок миллисекунд экономит и журнал, и внимание: чаще всё равно ничего не меняется.
Для плат, которые должны реагировать мгновенно, есть аппаратный путь: touchAttachInterrupt(TACH, обработчик, порог) — прерывание срабатывает, когда значение уходит ниже порога, и скетч может спать до события. Порог в третьем аргументе — тот же, что вы подобрали калибровкой, поэтому мой порядок всегда один: сначала Serial-калибровка, потом, если нужно, прерывание. Внутри обработчика — только установка volatile-флага, решения принимаются в loop: правила жизни обработчика прерывания собраны в уроке про антидребезг на прерывании — они переносятся на тач-вход один в один.
Покой уплывает. Нагрев платы на пару градусов, переклейка проводка, сезонная влажность в шкафу — уровень покоя гуляет на десятки процентов от исходного, и порог, настроенный однажды, через полгода лжёт. Поэтому калибровку я привязал к жизненному циклу: при каждом старте среднее из восьми чтений пересчитывается, а в длинных проектах добавил перекалибровку по команде из Serial — покой обновляется на ходу, порог пересчитывается от него, и скетч не нужно перешивать. Часы на стене рядом с этой полкой ушли на два с половиной процента покоя за зиму — мелочь, которой хватает, чтобы порог стал ложным.
Фантомы: провод, вода и чужие руки
Длинный провод — антенна. Сорок сантиметров рядом с сетевой проводкой или антенной самой платы собирают наводки, и график touchRead превращается в пилу — мой ночной случай был ровно таким: провалы ниже порога, свет сам собой. Лечение по нарастающей: укоротить провод, увести его от силовой проводки, подтянуть к земле через слабый резистор, и уже потом ужесточать софт — подтверждение тремя замерами вместо двух и более широкий гистерезис. Капля воды и влажная уборка дают длительное «касание»: мокрая тряпка по панели включала подсветку на всё время протирки — для уличных и влажных мест я либо меняю механику на оптику, либо ввожу ограничение: касание считается только после сухого покоя не короче минуты.
Отдельная глава — грань между «касание» и «присутствие». Датчик чувствует ладонь в паре сантиметров и сквозняк с каплей влаги, поэтому для сценариев «дотронулся — открыл» я ввёл вторую ступень: подтверждённое касание держится не меньше сотни миллисекунд и заканчивается отпусканием — только тогда считается событием. Без этого полка включалась от прохода мимо с полотенцем на плече. Такой двухтактный жест — касание и отпускание — занимает у меня место нажатия кнопки во всех скетчах с тач-входами, и код от этого выигрывает: событие становится дискретным, как щелчок механической кнопки, и дальше пишется ровно тем же автоматом.
И честное мнение напоследок: тач-вход — не замена тактильной кнопке там, где рядом шум и влага. Но там, где корпус сверлить нельзя — стекло шкафа, декоративная панель, растение в кашпо, — он вне конкуренции: проводок, капля клея, калибровка на месте. Для стационарных плат после калибровки я печатаю покой и порог в Serial при каждом старте — по уроку про Serial-монитор такие строки снимаются за минуту, а вместе со строкой причины пробуждения после сна из урока про глубокий сон дают полную картину входа без всяких приборов.
Итог: десять тач-пинов — это десять кнопок без сверления, но каждая со своим характером. Покройте их таблицей распиновки, откалибруйте на месте, поставьте порог долей покоя, добавьте гистерезис и подтверждение — и фантомы уходят. Всё то же самое работает и во сне: таймеру сна тач-вход иногда удобнее кнопки, а шильдик причины пробуждения честно расскажет, кто именно разбудил плату.
Домашнее задание: приклейте к T0 проводок длиной десять сантиметров, прогоните журнал сырых значений, выпишите свой покой и свой порог — и сравните с соседним стендом. Цифры будут разными, и это главный урок: у ёмкостного входа нет общих чисел, есть общая процедура. Следующим шагом перевесьте подсветку с кнопки на прерывание и попробуйте пробуждение из сна по касанию — причины пробуждения уже ждут вас в соседнем уроке.
- 1. Что возвращает touchRead()?
- 2. Палец поднесли к проводку на тач-пине. Как меняется значение?
- 3. Чем на DevKit-плате опасен T1?
- 4. Подсветка включается сама по ночам, провод тач-пина длинный. Что делать?
- 5. Как разбудить плату из глубокого сна касанием тач-пина?
- У исходного ESP32 десять тач-пинов T0-T9: T0=GPIO4, T1=GPIO0 (кнопка BOOT), T2=GPIO2, T5=GPIO12 — strap-пины среди них
- touchRead возвращает циклы заряда-разряда: палец добавляет ёмкость, значение падает ниже уровня покоя
- Порог — доля покоя конкретного стенда (я беру 60%), калибровка средним из восьми чтений при старте
- Гистерезис и подтверждение двумя замерами подряд убирают мигание и фантомы; длинный провод — антенна, лечится трассировкой
- touchAttachInterrupt ловит пересечение порога аппаратно; тач-вход будит плату из глубокого сна
Частые вопросы по теме урока
- Что возвращает touchRead()?
- Схема считает циклы заряда-разряда за окно измерения. Палец добавляет ёмкость, циклов укладывается меньше — значение падает. Число циклов заряда-разряда ёмкости входа за окно измерения
- Палец поднесли к проводку на тач-пине. Как меняется значение?
- Ёмкость больше — циклов меньше — число ниже. Поэтому порог ставят ниже уровня покоя, а не выше. Падает: палец добавляет ёмкость, заряжать дольше
- Чем на DevKit-плате опасен T1?
- T1 сидит на GPIO0, где стоит кнопка BOOT. Любое нажатие этой кнопки ваш скетч увидит как касание — закладывайте это в логику или выбирайте другой пин. Это GPIO0 с кнопкой BOOT: нажатие штатной кнопки читается как касание
- Подсветка включается сама по ночам, провод тач-пина длинный. Что делать?
- Провод — антенна: сначала механика и трассировка, потом софт. Гистерезис и два-три замера подряд режут одиночные провалы от наводок. Укоротить провод и увести от силовой проводки, добавить гистерезис и подтверждение замерами
- Как разбудить плату из глубокого сна касанием тач-пина?
- Тач-пины умеют будить плату: включаете разрешение перед сном, а после пробуждения читаете причину и видите шильдик TOUCHPAD. Порог — тот же, что подобран калибровкой. esp_sleep_enable_touchpad_wakeup, причина вернётся как ESP_SLEEP_WAKEUP_TOUCHPAD