Самый простой ввод — и самый частый источник странностей: висящий вход и дребезг контактов. Разбираемся с обоими.
Как работает
У тактовой кнопки четыре ножки, попарно соединённые внутри. Нажатие замыкает пары между собой. Схема подключения одна на все случаи: одна сторона — на вывод, другая — на GND, вход — INPUT_PULLUP. Отпущенная кнопка читается как HIGH (вход подтянут внутренним резистором к питанию), нажатая — как LOW.
Контакты при нажатии дребезжат 5–20 мс: вход успевает несколько раз переключиться. Для подсчёта нажатий нужен фильтр — игнорировать изменения в течение 20–50 мс после первого.
// Термометр, который помнит минимум и максимум после выключения.
//
// Обычные переменные живут, пока есть питание. EEPROM — отдельная
// память на 1 КБ внутри ATmega328, которая переживает выключение. В неё
// пишутся рекорды: минимум и максимум с момента последнего сброса.
// Писать часто нельзя — у ячейки около ста тысяч перезаписей, — поэтому
// EEPROM.put пишет только тогда, когда рекорд действительно побит.
// Кнопка — сбросить рекорды.
#include <DHT.h>
#include <EEPROM.h>
DHT dht(4, DHT22);
const int RESET_BTN = 2;
const int ADDR = 0;
const uint16_t MAGIC = 0xC0DE; // признак «в EEPROM наши данные, а не мусор»
struct Records { uint16_t magic; float lo, hi; } rec;
void save() { EEPROM.put(ADDR, rec); }
void resetRecords() {
rec = {MAGIC, 1000, -1000};
save();
Serial.println(F("рекорды сброшены"));
}
void setup() {
Serial.begin(9600);
pinMode(RESET_BTN, INPUT_PULLUP);
dht.begin();
EEPROM.get(ADDR, rec);
if (rec.magic != MAGIC) resetRecords();
else {
Serial.print(F("из EEPROM: мин "));
Serial.print(rec.lo, 1);
Serial.print(F(", макс "));
Serial.println(rec.hi, 1);
}
}
void loop() {
if (digitalRead(RESET_BTN) == LOW) { resetRecords(); delay(300); }
float t = dht.readTemperature();
if (!isnan(t)) {
bool changed = false;
if (t < rec.lo) { rec.lo = t; changed = true; }
if (t > rec.hi) { rec.hi = t; changed = true; }
if (changed) save();
Serial.print(t, 1);
Serial.print(F(" °C мин "));
Serial.print(rec.lo, 1);
Serial.print(F(" макс "));
Serial.print(rec.hi, 1);
Serial.println(changed ? F(" (записано в EEPROM)") : F(""));
}
delay(2000);
}
В железе
Ножки, соединённые внутри, — те, что смотрят друг на друга через длинную сторону корпуса. Кнопка, поставленная на макетку «не той стороной», замкнута всегда.
Частые ошибки
Вход без подтяжки (INPUT вместо INPUT_PULLUP) — отпущенная кнопка читается случайно, от касания пальцем провода.
Одно нажатие считается несколько раз — дребезг. Нужен фильтр по времени.
Кнопка подключена к двум соединённым внутри ножкам — не работает никогда (или работает всегда).
На ESP32 кнопка на GPIO34–39 с INPUT_PULLUP — у этих выводов нет внутренней подтяжки. Нужен внешний резистор 10 кОм.