Vibration switch SW-18010P phát hiện rung chấn, không phải nghiêng. Bên trong là lò xo kim loại bao quanh trụ dẫn điện — rung làm lò xo dao động, chạm vào trụ tạo ra các xung điện cực ngắn (micro giây đến mili giây). Phải dùng interrupt — polling sẽ bỏ sót toàn bộ sự kiện.
Nguyên Lý Hoạt Động
1. Cơ Chế Lò Xo Dẫn Điện
SW-18010P chứa một lò xo xoắn kim loại bao quanh trụ dẫn điện trung tâm:
SW-18010P — Cắt dọc:
Vỏ kim loại
┌───────────────────────────────┐
│ │
│ ╔═══╗ Lò xo (spring) │
│ ║ ║ │ ← Lò xo = terminal 1
│ ║ ● ║ Trụ trung tâm │ ← Trụ = terminal 2
│ ║ ║ │
│ ╚═══╝ │
│ │
└───────────────────────────────┘
Pin 1 Pin 2
(lò xo) (trụ trung tâm)
Khi TĨNH: lò xo không chạm trụ → HỞ mạch
Khi RUNG: lò xo dao động, chạm trụ trong tích tắc → ĐÓNG mạch → xung ngắn
Thời gian xung: Mỗi lần chạm thường chỉ kéo dài 50μs – 5ms — cực ngắn. digitalRead() trong loop() gần như chắc chắn bỏ sót. Bắt buộc dùng interrupt.
2. Độ Nhạy Rất Cao
SW-18010P nhạy hơn nhiều so với tilt switch:
- Tilt switch (SW-520D): chỉ phát hiện khi nghiêng đủ để bi lăn
- Vibration switch: chỉ cần chạm nhẹ vào bàn cũng đủ để lò xo dao động
Ứng dụng phù hợp: Phát hiện gõ cửa, chấn động, va đập nhẹ, rung từ máy móc, thay vì phát hiện hướng.
3. Module KY-002
Một số kit dùng KY-002 thay SW-18010P:
- KY-002: có IC khuếch đại + tụ lọc → ít bounce hơn
- SW-18010P bare: bounce nhiều hơn nhưng độ nhạy cao hơn
Cả hai dùng code giống nhau (interrupt + debounce).
Thông Số Kỹ Thuật
| Thông số | Giá trị |
|---|---|
| Điện áp tối đa | 12V |
| Dòng tối đa | 30mA |
| Loại | Cơ học (spring contact) |
| Thời gian xung | 50μs – 5ms |
| Độ nhạy | Rất cao (nhạy với rung nhỏ) |
| Output | Digital (ON/OFF), active LOW với pull-up |
Sơ Đồ Chân (Pinout)
Module cảm biến rung — Nhìn từ mặt trước:
┌──────────────────────────────────┐
│ [SW-18010P hoặc KY-002] │
│ [Pull-up 10kΩ] │
│ [LED trạng thái] │
└──────────────────────────────────┘
GND VCC S
(-) (3.3-5V) (Signal OUT)
| Chân | Ký hiệu | Mô tả |
|---|---|---|
| GND | – | Mass |
| VCC | + | Nguồn 3.3V-5V |
| S | Signal | Output — LOW khi phát hiện rung (active LOW) |
Logic output: Module có pull-up → khi yên tĩnh S = HIGH, khi rung tạo xung LOW ngắn.
Kết Nối Phần Cứng
Module với ESP32 DevKit V1
ESP32 DevKit V1 Vibration Module
───────────────────── ─────────────────
3V3 ─────────────────→ VCC
GND ─────────────────→ GND
GPIO4 (Input) ─────────→ S
GPIO4: interrupt-capable, không phải strapping pin.
Module với Arduino Uno
Arduino Uno Vibration Module
───────────────────── ─────────────────
5V ─────────────────→ VCC
GND ─────────────────→ GND
Pin 2 (INT0) ─────────→ S ← Bắt buộc dùng interrupt pin
Không dùng Pin khác — xung quá ngắn, chỉ Pin 2 (INT0) và Pin 3 (INT1) có hardware interrupt trên Uno.
Code Arduino IDE
Code Phát Hiện Rung Cơ Bản — Arduino Uno
/*
* Vibration Switch SW-18010P — Interrupt phát hiện rung
* Board: Arduino Uno
* Kết nối: VCC→5V, GND→GND, S→Pin2 (INT0)
*
* QUAN TRỌNG: Xung rung rất ngắn (~50μs-5ms)
* Bắt buộc dùng interrupt — không dùng digitalRead() trong loop()
*
* active LOW: xung xuống LOW khi phát hiện rung
*/
const int VIB_PIN = 2;
const int LED_PIN = 13;
volatile int vibrationCount = 0; // Đếm số xung rung
volatile bool vibDetected = false;
// ISR — gọi khi có xung LOW (rung)
void onVibration() {
vibrationCount++;
vibDetected = true;
}
void setup() {
Serial.begin(9600);
pinMode(VIB_PIN, INPUT); // Module đã có pull-up
pinMode(LED_PIN, OUTPUT);
// Trigger trên FALLING edge: HIGH→LOW khi rung
attachInterrupt(digitalPinToInterrupt(VIB_PIN), onVibration, FALLING);
Serial.println("=== Vibration Switch SW-18010P ===");
Serial.println("Gõ nhẹ vào bàn để test...");
}
void loop() {
if (vibDetected) {
vibDetected = false;
digitalWrite(LED_PIN, HIGH); // Sáng khi phát hiện rung
Serial.print("RUN! Tổng xung: ");
Serial.println(vibrationCount);
delay(200); // Giữ LED sáng 200ms để nhìn thấy
digitalWrite(LED_PIN, LOW);
}
}
Code Đếm Rung Trong Cửa Sổ Thời Gian — Arduino Uno
/*
* Vibration Switch — Đếm xung rung trong 1 giây
* Ứng dụng: phân biệt rung nhẹ vs rung mạnh
* Board: Arduino Uno
* Kết nối: VCC→5V, GND→GND, S→Pin2
*/
const int VIB_PIN = 2;
const int LED_PIN = 13;
const unsigned long WINDOW_MS = 1000; // Cửa sổ 1 giây
volatile int pulseCount = 0;
unsigned long windowStart = 0;
void onVibration() {
pulseCount++;
}
void setup() {
Serial.begin(9600);
pinMode(VIB_PIN, INPUT);
pinMode(LED_PIN, OUTPUT);
attachInterrupt(digitalPinToInterrupt(VIB_PIN), onVibration, FALLING);
windowStart = millis();
Serial.println("Vibration Rate Monitor (xung/giây)");
Serial.println("Gõ nhẹ / mạnh / rung để xem sự khác biệt");
}
void loop() {
if (millis() - windowStart >= WINDOW_MS) {
// Tắt interrupt tạm để đọc pulseCount an toàn
noInterrupts();
int count = pulseCount;
pulseCount = 0;
interrupts();
windowStart = millis();
// Phân loại mức rung
String level;
if (count == 0) level = "Yên tĩnh";
else if (count < 5) level = "Rung nhẹ";
else if (count < 20) level = "Rung vừa";
else level = "Rung MẠNH";
// Hiển thị bar chart ngắn
Serial.print(count);
Serial.print(" xung/s → ");
Serial.print(level);
Serial.print(" [");
int barLen = min(count, 30);
for (int i = 0; i < barLen; i++) Serial.print("█");
Serial.println("]");
// LED sáng tỉ lệ với độ rung
digitalWrite(LED_PIN, count > 2 ? HIGH : LOW);
}
}
Code Secret Knock — Nhận Dạng Pattern Gõ Bí Mật (Arduino Uno)
/*
* Vibration Switch — Secret Knock: nhận dạng pattern gõ
* Gõ đúng 3 tiếng (ngắn - dài - ngắn) trong 2 giây → unlock
* Board: Arduino Uno
* Kết nối: VCC→5V, GND→GND, S→Pin2, LED xanh→Pin9, LED đỏ→Pin10
*/
const int VIB_PIN = 2;
const int LED_OK = 9; // Xanh — unlock thành công
const int LED_FAIL = 10; // Đỏ — sai pattern
// Pattern mục tiêu: 3 gõ với khoảng cách
// Gõ 1, chờ ~200ms, Gõ 2, chờ ~600ms, Gõ 3
const int PATTERN_KNOCKS = 3;
const int PATTERN_TIMING[] = {0, 250, 650}; // ms sau lần gõ đầu tiên
const int TIMING_TOLERANCE = 150; // ±150ms chấp nhận được
volatile bool knockDetected = false;
unsigned long knockTimes[10]; // Lưu thời điểm mỗi tiếng gõ
int knockCount = 0;
unsigned long lastKnockTime = 0;
bool listening = false;
void onKnock() {
if (!listening) return;
unsigned long now = millis();
// Debounce 80ms
if (now - lastKnockTime > 80) {
lastKnockTime = now;
knockDetected = true;
}
}
void setup() {
Serial.begin(9600);
pinMode(VIB_PIN, INPUT);
pinMode(LED_OK, OUTPUT);
pinMode(LED_FAIL, OUTPUT);
attachInterrupt(digitalPinToInterrupt(VIB_PIN), onKnock, FALLING);
Serial.println("=== Secret Knock System ===");
Serial.println("Pattern: GÕ - chờ 250ms - GÕ - chờ 650ms - GÕ");
startListening();
}
void startListening() {
knockCount = 0;
listening = true;
Serial.println("\nĐang chờ gõ cửa...");
}
bool checkPattern() {
if (knockCount != PATTERN_KNOCKS) return false;
for (int i = 1; i < knockCount; i++) {
int actualGap = knockTimes[i] - knockTimes[0];
int targetGap = PATTERN_TIMING[i];
if (abs(actualGap - targetGap) > TIMING_TOLERANCE) return false;
}
return true;
}
void loop() {
if (knockDetected && listening) {
knockDetected = false;
if (knockCount < 10) {
knockTimes[knockCount++] = millis();
Serial.print("Gõ #"); Serial.println(knockCount);
}
}
// Timeout: 2.5 giây sau lần gõ cuối → kiểm tra pattern
if (knockCount > 0 && millis() - knockTimes[knockCount-1] > 2500) {
listening = false;
if (checkPattern()) {
Serial.println("✓ ĐÚNG! Mở khóa...");
for (int i = 0; i < 3; i++) {
digitalWrite(LED_OK, HIGH); delay(200);
digitalWrite(LED_OK, LOW); delay(100);
}
} else {
Serial.println("✗ SAI pattern. Thử lại.");
digitalWrite(LED_FAIL, HIGH); delay(1000);
digitalWrite(LED_FAIL, LOW);
}
delay(500);
startListening();
}
}
Code ESP32 — Vibration Alarm Non-Blocking
/*
* Vibration Switch — ESP32, báo động rung non-blocking
* Board: ESP32 DevKit V1
* Kết nối: VCC→3V3, GND→GND, S→GPIO4
* Buzzer→GPIO5 (active buzzer)
*/
const int VIB_PIN = 4;
const int BUZZER_PIN = 5;
// Ngưỡng: cần ≥ N xung trong X giây mới coi là "rung thật"
const int MIN_PULSES = 3;
const unsigned long DETECT_WINDOW_MS = 500;
volatile int pulseCount = 0;
unsigned long windowStart = 0;
bool alarmOn = false;
unsigned long alarmStartTime = 0;
const unsigned long ALARM_DURATION_MS = 5000; // Báo 5 giây
void IRAM_ATTR onVibISR() {
pulseCount++;
}
void setup() {
Serial.begin(115200);
pinMode(VIB_PIN, INPUT);
pinMode(BUZZER_PIN, OUTPUT);
digitalWrite(BUZZER_PIN, LOW);
attachInterrupt(digitalPinToInterrupt(VIB_PIN), onVibISR, FALLING);
windowStart = millis();
Serial.println("=== Vibration Alarm ESP32 ===");
}
void loop() {
unsigned long now = millis();
// Kiểm tra cửa sổ thời gian
if (now - windowStart >= DETECT_WINDOW_MS) {
noInterrupts();
int count = pulseCount;
pulseCount = 0;
interrupts();
windowStart = now;
// Nếu đủ xung → kích hoạt alarm
if (count >= MIN_PULSES && !alarmOn) {
alarmOn = true;
alarmStartTime = now;
Serial.printf("PHÁT HIỆN RUNG! %d xung/0.5s → Báo động\n", count);
}
}
// Quản lý buzzer alarm
if (alarmOn) {
if (now - alarmStartTime < ALARM_DURATION_MS) {
// Beep nhanh khi đang báo
bool buzzerState = ((now / 100) % 2 == 0);
digitalWrite(BUZZER_PIN, buzzerState ? HIGH : LOW);
} else {
// Hết thời gian → tắt alarm
alarmOn = false;
digitalWrite(BUZZER_PIN, LOW);
Serial.println("Alarm tắt.");
}
}
}
Kết Quả Mong Đợi
=== Vibration Switch SW-18010P ===
Gõ nhẹ vào bàn để test...
RUN! Tổng xung: 1
RUN! Tổng xung: 5
RUN! Tổng xung: 12
Ứng Dụng Thực Tế
| Ứng dụng | Chi tiết |
|---|---|
| Báo động rung xe | Cảnh báo khi xe bị động vào |
| Gõ cửa thông minh | Gõ N lần → gửi thông báo |
| Mẫu gõ bí mật | Unlock bằng pattern gõ |
| Theo dõi rung máy | Cảnh báo máy móc rung bất thường |
| Đếm bước chân | Gắn ở giày, đếm mỗi bước |
Lưu Ý Khi Sử Dụng
1. Phải dùng interrupt — không có cách khác
Xung kéo dài 50μs-5ms. loop() Arduino chạy ~100μs/vòng → hoàn toàn có thể bỏ sót. Không có interrupt → code không đáng tin cậy.
2. Bounce rất nhiều từ 1 lần rung
Một lần gõ cửa có thể tạo 5-20 xung do lò xo dao động. Xử lý bằng: đếm xung trong cửa sổ thời gian thay vì đếm “sự kiện”, hoặc debounce 80-100ms.
3. Nhiễu từ môi trường
Trong nhà có nhiều nguồn rung: xe tải đi qua, HVAC, người đi bộ. Tăng MIN_PULSES ngưỡng để giảm false positive.
4. noInterrupts() / interrupts() khi đọc biến volatile
pulseCount là biến 16-bit (int) — đọc không atomic trên 8-bit AVR. Luôn tắt interrupt khi đọc+reset để tránh race condition.


