← До всіх проєктів
Сенсори Середній

Датчик вібрації SW-420: рахуємо удари перериванням і не плутаємо брязкіт з подіями

Один удар по столу — це не один імпульс, а десятки: пружинний контакт усередині SW-420 бринить. Ловимо фронти перериванням, групуємо їх у події за паузою 200 мс і отримуємо лічильник стуків для сигналізації чи «секретного» замка.

Складність
Середній
Час
40 min
Категорія
Сенсори
Оновлено

Про проєкт

SW-420 — металевий циліндр з пружиною і контактом усередині, плюс компаратор LM393 на платі модуля. У спокої контакт замкнений і вихід DO тримається в LOW; від поштовху пружина відскакує, і на DO з'являються короткі імпульси HIGH. Датчик не міряє силу вібрації, зате ловить удари по столу, дверях чи шухляді — для сигналізації, лічильника відкривань або стукотливого замка.

Що потрібно

  • ESP32 DevKit (або Arduino)
  • Модуль SW-420
  • Світлодіод і резистор 220 Ом

Схема

Модуль ESP32
VCC 3V3
GND GND
DO GPIO16
Світлодіод GPIO26 → 220 Ом → GND

Підстроювальний резистор на модулі задає поріг компаратора — тобто яку тривалість розмикання контакту модуль вважає вібрацією.

Чому один удар — це багато імпульсів

Пружина після удару коливається ще 100–200 мс, і кожне розмикання дає окремий фронт. Якщо рахувати фронти, «один стук» виявиться «двадцятьма». Правильно так:

  1. Переривання на кожен фронт RISING — нічого не пропускаємо, навіть коротенькі імпульси по пів мілісекунди, які digitalRead() у loop() легко проґавив би.
  2. Групування за паузою: подія закінчилась, коли 200 мс не було жодного фронту.

Код

// Датчик стуку: SW-420 + переривання, «брязкіт» групуємо в окремі події
const int VIB_PIN = 16;
const int LED_PIN = 26;

volatile unsigned long lastEdgeMs = 0;
volatile int edges = 0;

void IRAM_ATTR onVibration() {
  edges++;
  lastEdgeMs = millis();
}

int knocks = 0;

void setup() {
  Serial.begin(115200);
  pinMode(VIB_PIN, INPUT);
  pinMode(LED_PIN, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(VIB_PIN), onVibration, RISING);
}

void loop() {
  // стук — це пачка фронтів; вона закінчилась, коли 200 мс тиша
  if (edges > 0 && millis() - lastEdgeMs > 200) {
    noInterrupts();
    int n = edges;
    edges = 0;
    interrupts();
    knocks++;
    Serial.printf("Стук №%d (%d фронтів)\n", knocks, n);
    digitalWrite(LED_PIN, HIGH);
    delay(100);
    digitalWrite(LED_PIN, LOW);
  }
}

Клацніть по датчику в симуляторі — це «удар». У моніторі порту видно, скільки фронтів дав один стук, а лічильник росте на одиницю.

Чому noInterrupts()

edges змінюється в перериванні. Якщо переривання спрацює між читанням edges і його обнуленням, один фронт загубиться. Короткий блок noInterrupts() … interrupts() робить ці дві дії неподільними. На ESP32 з двома ядрами надійніше використовувати portMUX_TYPE і portENTER_CRITICAL(), але для лічильника стуків вистачає й цього.

Ідеї

  • Секретний стук: запам'ятайте інтервали між трьома–п'ятьма стуками і відкривайте замок (сервопривід) лише при схожому ритмі з допуском ±25 %.
  • Датчик пральної машини: рахуйте події за хвилину. Вібрація припинилася на 5 хвилин — прання завершене, надсилайте повідомлення в Telegram.
  • На Arduino Uno переривання є лише на D2 і D3 — у симуляторі варіант для Uno сам переносить датчик на D2.