Как сделать задержку без delay — millis
Шаблон «делать что-то раз в N миллисекунд», который не останавливает скетч:
const unsigned long INTERVAL = 1000; // каждые 1000 мс
unsigned long previous = 0;
void setup() {
Serial.begin(9600);
}
void loop() {
if (millis() - previous >= INTERVAL) {
previous = millis();
Serial.println("Прошла секунда"); // ваше действие
}
// остальной код выполняется без остановки
}millis() — сколько миллисекунд прошло с запуска платы. Скетч на каждом проходе loop проверяет, прошёл ли интервал с прошлого раза; если нет — сразу идёт дальше. Поэтому кнопки, датчики и команды из монитора порта обрабатываются всё время, а не после окончания паузы.
Однократная задержка после события
Например, нажали кнопку — светодиод горит 5 секунд и гаснет, а кнопку можно нажать снова в любой момент:
const int BUTTON = 2;
const unsigned long ON_TIME = 5000;
unsigned long startTime = 0;
bool ledOn = false;
void setup() {
pinMode(BUTTON, INPUT_PULLUP);
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
if (digitalRead(BUTTON) == LOW) { // нажали — включить и запомнить время
digitalWrite(LED_BUILTIN, HIGH);
ledOn = true;
startTime = millis();
}
if (ledOn && millis() - startTime >= ON_TIME) { // прошло 5 секунд — выключить
digitalWrite(LED_BUILTIN, LOW);
ledOn = false;
}
}Пока кнопка нажата, отсчёт всё время начинается заново, — так работает свет на лестнице с датчиком движения.
Несколько интервалов
Каждому делу — своя переменная «время прошлого раза»:
unsigned long lastSensor = 0, lastDisplay = 0;
void setup() {
Serial.begin(9600);
}
void loop() {
unsigned long now = millis();
if (now - lastSensor >= 2000) { // раз в 2 секунды
lastSensor = now;
Serial.println("Чтение датчика");
}
if (now - lastDisplay >= 500) { // раз в полсекунды
lastDisplay = now;
Serial.println("Обновление экрана");
}
}Частые ошибки
- Время в
int. На Unointвмещает только до 32 767 — это 32,7 секунды в миллисекундах. Храните время только вunsigned long. - Сравнение суммы вместо разности. Пишите
millis() - previous >= INTERVAL, а неmillis() >= previous + INTERVAL: первый вариант верно работает и после переполненияmillis(), которое случается примерно через 49,7 суток работы. ==вместо>=. Условиеmillis() - previous == 1000может «проскочить», если проходloopзанял больше миллисекунды. Нужно>=.- Длинный
delayв том жеloop. Если рядом осталсяdelay(5000), вся точность пропадает: проверка выполнится только после него.
Подробный разбор, почему вычитание переживает переполнение, — в уроке millis вместо delay.