Курс Arduino с нуля · Датчики

Урок 16. Датчики наклона и вибрации: сигнализация

Есть датчики, которые не измеряют, а просто замыкаются. Учимся ловить событие длиной в миллисекунду, считать стуки и собираем сигнализацию.

Урок 16 из 31⏱ примерно 55 минутОбновлено: 26 июля 2026

Что понадобится

  • Плата Uno из набора
  • Кабель USB
  • Макетная плата
  • Датчик наклона
  • Датчик вибрации
  • 2 светодиода
  • 2 резистора 470 Ом
  • 5 проводов «папа-папа»

Все детали есть в обучающем наборе, по которому идёт курс.

Крутилка и фоторезистор выдавали число — плавную величину, которую нужно измерять. Но огромная часть датчиков устроена иначе: они просто замыкаются, когда что-то произошло. По сути это кнопки — только нажимает их не палец, а мир вокруг.

Сегодня возьмём сразу два таких датчика из набора: наклона и вибрации. Оба подключаются как обычная кнопка, а вот программы для них нужны совсем разные — и это самое интересное.

Знакомимся с деталями

Датчик вибрацииHDXSVвнутри — трубка с шарикамив покое ЗАМКНУТ,при тряске размыкаетсяставить под небольшим наклономДатчик наклонаSTвнутри — два шарика разного размеранаклонили в одну сторону — замкнут,в другую — разомкнутполярности нет ни у того, ни у другого
Два маленьких цилиндрика с двумя ножками. Ни у одного нет полярности.

Датчик вибрации — синий цилиндрик с надписью на боку. Внутри полая трубка и несколько металлических шариков. В покое шарики лежат и замыкают выводы, а от толчка подпрыгивают, и контакт на мгновение рвётся. Обозначают его буквами SV.

Датчик наклона — зелёный цилиндрик. Внутри два шарика разного размера, которые перекатываются при повороте детали. Обозначение — ST.

Наклон влевоконтакт замкнутшарики достали до выводаНаклон вправоконтакт разомкнутшарики откатилисьЭто кнопка,которую нажимаетсила тяжести
Наклонили в одну сторону — шарики замкнули контакт; в другую — разомкнули.

Важная мелочь для датчика вибрации. Его нужно ставить с небольшим наклоном, градусов на 10–30. Строго вертикально или строго горизонтально шарики лежат неустойчиво, и датчик начинает срабатывать сам по себе.

Собираем схему

Раз оба датчика — выключатели, подключаем их точно так же, как кнопку в одиннадцатом уроке: один вывод на цифровой вход с INPUT_PULLUP, другой на землю. Никаких внешних резисторов.

++abcdefghij110203049131618222527123451Датчик наклона (зелёный): из шины «−» в столбик 4. Полярности нет.2Датчик вибрации (синий): из шины «−» в столбик 9. Ставьте его с небольшим наклоном.3Сигнальные провода: вывод 2 → столбик 4, вывод 3 → столбик 9 (ряд a).4Два светодиода с резисторами 470 Ом: на выводах 10 и 9.5Земля: GND платы → шина «−», к которой подключены оба датчика и оба светодиода.
Наклон — на вывод 2, вибрация — на вывод 3, два светодиода — на 10 и 9.
UnoD2D3D10D9GNDST1 наклонSV1 вибрацияVD1R1 470 ОмVD2R2 470 ОмОба датчика подключены как кнопки: один конец на вывод платы, другой на землю.
На чертеже видно, что оба датчика включены совершенно одинаково — как две кнопки.

Проверьте перед включением: каждый датчик одним концом сидит в шине «−», другим — в своём столбике; из этого же столбика идёт провод на плату; светодиоды стоят длинной ножкой к своим резисторам.

Шаг 1. Слушаем оба датчика

Как всегда с новой деталью — сначала просто смотрим, что она говорит:

const int TILT = 2;    // датчик наклона
const int VIBRO = 3;   // датчик вибрации

void setup() {
  Serial.begin(9600);

  // Оба датчика — обычные замыкатели, подключаем как кнопки.
  pinMode(TILT, INPUT_PULLUP);
  pinMode(VIBRO, INPUT_PULLUP);

  Serial.println("наклон   вибрация");
}

void loop() {
  Serial.print(digitalRead(TILT));
  Serial.print("        ");
  Serial.println(digitalRead(VIBRO));
  delay(100);
}

Откройте монитор порта. Наклоняйте макетную плату из стороны в сторону — первое число будет переключаться между 1 и 0. Стукните пальцем по столу — второе число на мгновение мигнёт единицей.

Обратите внимание на важную деталь: в покое датчик вибрации показывает 0 — он замкнут. Единица появляется, только когда его тряхнули. Логика обратная привычной, и это нормально.

Шаг 2. Наклон: определяем сторону

Наклон — «медленный» датчик: его состояние держится сколько угодно, пока деталь лежит в одном положении. С таким работать просто:

const int TILT = 2;
const int LED_A = 10;
const int LED_B = 9;

void setup() {
  pinMode(TILT, INPUT_PULLUP);
  pinMode(LED_A, OUTPUT);
  pinMode(LED_B, OUTPUT);
}

void loop() {
  // Шарики внутри датчика замкнули контакт — получаем LOW.
  bool tilted = (digitalRead(TILT) == LOW);

  digitalWrite(LED_A, tilted);
  digitalWrite(LED_B, !tilted);
}

Наклоните плату влево — горит один светодиод, вправо — другой. Одна строка digitalWrite(LED_B, !tilted) делает второй светодиод противоположным первому: восклицательный знак означает «наоборот».

Такие датчики стоят в «умных» утюгах: если утюг долго не меняет положение, значит, им никто не пользуется, — и он выключается сам.

Шаг 3. Вибрация: событие длиной в миллисекунду

А вот с вибрацией всё сложнее. Попробуйте написать «если тряхнули — зажги светодиод» — и вы почти ничего не увидите. Толчок длится единицы миллисекунд, светодиод вспыхивает и гаснет быстрее, чем глаз успевает заметить.

разомкнутзамкнутчто выдаёт датчик при одном ударе по столувсего несколько миллисекундсветодиодвспышка держится 300 мс — её видноПриём: запомнить момент толчка и светить, пока с него прошло мало времени.
Один удар по столу даёт несколько коротких импульсов. Их нужно превратить в одну заметную вспышку.

Приём тот же, что и в антидребезге, только наоборот: там мы сокращали лишние события, а здесь растягиваем одно короткое. Запоминаем момент последнего толчка и светим, пока с него прошло мало времени:

const int VIBRO = 3;
const int LED = 10;

// Сколько держать свет после последнего толчка.
const unsigned long FLASH_MS = 300;

unsigned long lastShake = 0;

void setup() {
  pinMode(VIBRO, INPUT_PULLUP);
  pinMode(LED, OUTPUT);
}

void loop() {
  // В покое датчик замкнут и даёт LOW. Тряхнули — контакт разорвался,
  // и на мгновение появился HIGH.
  if (digitalRead(VIBRO) == HIGH) {
    lastShake = millis();
  }

  // Свет горит, пока с последнего толчка прошло меньше FLASH_MS.
  if (millis() - lastShake < FLASH_MS) {
    digitalWrite(LED, HIGH);
  } else {
    digitalWrite(LED, LOW);
  }
}

Постучите пальцем по столу рядом с макеткой — светодиод отчётливо вспыхивает на треть секунды. Поменяйте FLASH_MS на 2000 — получится «свет по хлопку», который горит две секунды.

Почему lastShake обновляется, а не сравнивается? Потому что толчков за один удар много. Каждый из них просто сдвигает момент «последнего события» вперёд, и вспышка продлевается. Получается очень надёжно: сколько бы импульсов ни пришло, свет горит ровно заданное время после последнего.

Шаг 4. Два стука — включить свет

Раз мы умеем ловить удары, можно считать их и реагировать на комбинации. Сделаем «умный выключатель»: два стука по столу переключают свет.

const int VIBRO = 3;
const int LED = 10;

const unsigned long IGNORE_MS = 120;   // дрожание одного удара не считаем
const unsigned long WINDOW_MS = 700;   // за столько нужно успеть стукнуть дважды

unsigned long lastKnock = 0;
int knocks = 0;
bool lampOn = false;

void setup() {
  pinMode(VIBRO, INPUT_PULLUP);
  pinMode(LED, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  // Новый стук: датчик разомкнулся, и с прошлого стука прошло достаточно.
  if (digitalRead(VIBRO) == HIGH && millis() - lastKnock > IGNORE_MS) {
    knocks = knocks + 1;
    lastKnock = millis();

    Serial.print("стук № ");
    Serial.println(knocks);
  }

  // Пауза затянулась — подводим итог серии.
  if (knocks > 0 && millis() - lastKnock > WINDOW_MS) {
    if (knocks >= 2) {
      lampOn = !lampOn;
      digitalWrite(LED, lampOn);
      Serial.println("два стука — переключаю свет");
    }
    knocks = 0;
  }
}

Здесь работают сразу два интервала времени, и у каждого своя задача:

  • IGNORE_MS — «слепое» время после удара. Один настоящий удар даёт россыпь импульсов; за 120 мс они успевают закончиться, и мы не считаем один стук за пять.
  • WINDOW_MS — сколько ждём продолжения. Если после последнего стука прошло 700 мс и новых нет, серия считается законченной: два стука были — переключаем, один — забываем.

Стучите по столу, а не по самой плате: датчик отлично ловит вибрацию через столешницу, а от прямых ударов по макетке провода вылетают из отверстий.

Шаг 5. Собираем сигнализацию

Теперь соединим оба датчика в одно устройство. Логика настоящей сигнализации: при включении она запоминает, как вещь лежала, и поднимает тревогу, если вещь сдвинули или потрясли.

const int TILT = 2;
const int VIBRO = 3;
const int LED_ARMED = 9;    // зелёный: «на охране»
const int LED_ALARM = 10;   // красный: тревога

const unsigned long ALARM_MS = 5000;   // сколько тревожиться после последнего толчка

int calmTilt = HIGH;        // положение датчика наклона в спокойном состоянии
bool alarmOn = false;
unsigned long alarmStart = 0;
unsigned long lastBlink = 0;
bool blinkOn = false;

void setup() {
  pinMode(TILT, INPUT_PULLUP);
  pinMode(VIBRO, INPUT_PULLUP);
  pinMode(LED_ARMED, OUTPUT);
  pinMode(LED_ALARM, OUTPUT);
  Serial.begin(9600);

  delay(1000);                    // даём вещи улечься
  calmTilt = digitalRead(TILT);   // запоминаем, как она лежала
  Serial.println("Охрана включена.");
}

void loop() {
  bool moved = (digitalRead(TILT) != calmTilt);
  bool shaken = (digitalRead(VIBRO) == HIGH);

  if (moved || shaken) {
    if (!alarmOn) {
      Serial.println("ТРЕВОГА!");
    }
    alarmOn = true;
    alarmStart = millis();
  }

  if (alarmOn && millis() - alarmStart > ALARM_MS) {
    alarmOn = false;
    Serial.println("тихо, снова на охране");
  }

  if (alarmOn) {
    digitalWrite(LED_ARMED, LOW);

    if (millis() - lastBlink >= 120) {
      blinkOn = !blinkOn;
      digitalWrite(LED_ALARM, blinkOn);
      lastBlink = millis();
    }
  } else {
    digitalWrite(LED_ALARM, LOW);
    digitalWrite(LED_ARMED, HIGH);
  }
}

Обратите внимание на строку calmTilt = digitalRead(TILT) в setup(). Мы не задаём положение заранее — устройство запоминает его само при включении. Тот же приём, что и с калибровкой датчика света: прибор подстраивается под обстановку, а не требует настройки.

Положите макетку на книгу, дождитесь, пока загорится зелёный «на охране», и аккуратно сдвиньте книгу — красный замигает, а в мониторе появится «ТРЕВОГА!». Через пять секунд покоя устройство само вернётся на охрану.

Где такое встречается. Ровно так работает сигнализация в автомобиле: датчик наклона ловит попытку поднять машину домкратом, датчик удара — разбитое стекло. Разница только в чувствительности и в том, что вместо светодиода там сирена.

Не работает? Разбираемся

  • Датчик вибрации срабатывает без конца. Скорее всего, он стоит слишком вертикально или слишком горизонтально. Наклоните его на 10–30 градусов — шарики займут устойчивое положение.
  • Датчик вибрации не срабатывает совсем. Проверьте, что в setup() стоит INPUT_PULLUP, и что вы ищете HIGH, а не LOW: в покое этот датчик замкнут.
  • Наклон не переключается. Поворачивайте деталь вокруг её оси — датчик чувствует наклон только в одной плоскости. Если не помогает, проверьте провод из столбика датчика на плату.
  • Светодиод от стука еле заметен. Значит, вспышка не растянута: FLASH_MS слишком маленький или программа зажигает светодиод прямо в if, без запоминания времени.
  • Один стук считается за несколько. Увеличьте IGNORE_MS до 200 мс: у разных датчиков «звон» длится по-разному.
  • Сигнализация срабатывает сразу после включения. Плата запомнила положение, пока вы ещё держали макетку в руках. Дайте ей полежать: секунда паузы в setup() как раз для этого, при необходимости увеличьте её.

Задания для тренировки

  • РазминкаСделайте «датчик уровня»: пусть светодиод горит, только когда макетка лежит ровно, и гаснет при любом наклоне.
  • ПосложнееНаучите устройство различать три стука и включать от них другой режим — например, мигание. Подсказка: у вас уже есть счётчик, добавьте ещё одну проверку в конце серии.
  • Со звёздочкой ⭐Сделайте сигнализацию честной: пусть тревога не гаснет сама, а снимается кнопкой — как настоящая. Разбор ниже.
Показать разбор «сигнализация с кнопкой»

Добавьте на макетку кнопку из прошлых уроков: один вывод на цифровой вход 4, другой — в ту же шину «−». Программа меняется в двух местах: тревога больше не выключается по времени, зато появляется кнопка, которая её снимает.

const int TILT = 2;
const int VIBRO = 3;
const int BUTTON = 4;       // кнопка снятия с охраны
const int LED_ARMED = 9;
const int LED_ALARM = 10;

const unsigned long BOUNCE_MS = 30;

int calmTilt = HIGH;
bool alarmOn = false;
unsigned long lastBlink = 0;
bool blinkOn = false;

int wasState = HIGH;
unsigned long lastChange = 0;

bool buttonPressed() {
  int nowState = digitalRead(BUTTON);
  bool pressed = false;

  if (nowState != wasState) {
    if (millis() - lastChange > BOUNCE_MS && nowState == LOW) {
      pressed = true;
    }
    lastChange = millis();
    wasState = nowState;
  }

  return pressed;
}

void setup() {
  pinMode(TILT, INPUT_PULLUP);
  pinMode(VIBRO, INPUT_PULLUP);
  pinMode(BUTTON, INPUT_PULLUP);
  pinMode(LED_ARMED, OUTPUT);
  pinMode(LED_ALARM, OUTPUT);
  Serial.begin(9600);

  delay(1000);
  calmTilt = digitalRead(TILT);
  Serial.println("Охрана включена.");
}

void loop() {
  bool moved = (digitalRead(TILT) != calmTilt);
  bool shaken = (digitalRead(VIBRO) == HIGH);

  if (moved || shaken) {
    if (!alarmOn) {
      Serial.println("ТРЕВОГА!");
    }
    alarmOn = true;
  }

  // Тревога снимается только руками — и заодно заново запоминаем положение.
  if (buttonPressed() && alarmOn) {
    alarmOn = false;
    calmTilt = digitalRead(TILT);
    Serial.println("снято с охраны, ставлю заново");
  }

  if (alarmOn) {
    digitalWrite(LED_ARMED, LOW);

    if (millis() - lastBlink >= 120) {
      blinkOn = !blinkOn;
      digitalWrite(LED_ALARM, blinkOn);
      lastBlink = millis();
    }
  } else {
    digitalWrite(LED_ALARM, LOW);
    digitalWrite(LED_ARMED, HIGH);
  }
}

Функция buttonPressed — та самая, из одиннадцатого урока, скопирована без единой правки. Так и работают взрослые программы: приём, который вы однажды разобрали, дальше просто переносится из проекта в проект.

И одна тонкость: при снятии с охраны мы заново запоминаем положение датчика наклона. Иначе, если вещь оставили в новом положении, тревога включится сразу же снова.

Что мы узнали

  • Датчики наклона и вибрации — это выключатели. Подключаются как кнопки, через INPUT_PULLUP, и не требуют внешних резисторов.
  • Датчик вибрации в покое замкнут, а размыкается от толчка — логика обратная привычной.
  • Короткое событие нужно растягивать: запомнить момент и держать реакцию, пока с него прошло мало времени.
  • Серию быстрых импульсов от одного удара гасят «слепым» интервалом, а комбинации (два стука, три стука) считают в окне времени.
  • Хороший прибор сам запоминает исходное состояние при включении, а не требует настройки вручную.

Остался последний датчик набора — и самый необычный. Он видит то, чего не видит никто: магнитное поле. В следующем уроке соберём детектор магнитов, научимся отличать северный полюс от южного и сделаем датчик открытой двери.

Частые вопросы

Как работает датчик вибрации?

Внутри полой трубки лежат металлические шарики, которые в покое замыкают выводы. От толчка они подпрыгивают, и контакт на несколько миллисекунд рвётся. То есть в покое датчик замкнут, а при тряске размыкается — логика обратная привычной.

Почему датчик вибрации срабатывает сам по себе?

Скорее всего, он стоит строго вертикально или строго горизонтально — шарики лежат неустойчиво. Датчик нужно расположить под небольшим углом, примерно 10–30 градусов.

Как поймать событие, которое длится миллисекунду?

Запомнить момент события в переменную и держать реакцию, пока с него прошло мало времени. Тогда короткий импульс превращается в заметную вспышку, а серия импульсов от одного удара просто продлевает её.

Нужны ли этим датчикам резисторы?

Нет. Оба датчика — обычные замыкатели, поэтому их подключают как кнопку: один вывод на цифровой вход в режиме INPUT_PULLUP, другой на землю. Подтягивающий резистор уже встроен в плату.

← Все уроки курса