Датчик вібрації SW-420: рахуємо удари перериванням і не плутаємо брязкіт з подіями
Один удар по столу — це не один імпульс, а десятки: пружинний контакт усередині SW-420 бринить. Ловимо фронти перериванням, групуємо їх у події за паузою 200 мс і отримуємо лічильник стуків для сигналізації чи «секретного» замка.
Про проєкт
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 мс, і кожне розмикання дає окремий фронт. Якщо рахувати фронти, «один стук» виявиться «двадцятьма». Правильно так:
- Переривання на кожен фронт RISING — нічого не пропускаємо, навіть коротенькі імпульси по пів мілісекунди, які
digitalRead()уloop()легко проґавив би. - Групування за паузою: подія закінчилась, коли 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.