The SW-420 Vibration Sensor: Counting Knocks With an Interrupt Without Confusing Bounce With Events
One knock on a table is not one pulse but dozens: the spring contact inside the SW-420 rattles. We catch the edges with an interrupt, group them into events by a 200 ms pause, and get a knock counter for an alarm or a secret-knock lock.
About the project
The SW-420 is a metal cylinder with a spring and a contact inside, plus an LM393 comparator on the module board. At rest the contact is closed and DO stays LOW; a jolt makes the spring bounce, and short HIGH pulses appear on DO. The sensor does not measure how strong a vibration is, but it catches knocks on a table, a door or a drawer — for an alarm, an opening counter or a secret-knock lock.
What you need
- ESP32 DevKit (or an Arduino)
- An SW-420 module
- An LED and a 220 Ω resistor
Wiring
| Module | ESP32 |
|---|---|
| VCC | 3V3 |
| GND | GND |
| DO | GPIO16 |
| LED | GPIO26 → 220 Ω → GND |
The trimmer on the module sets the comparator threshold — in other words, how long the contact has to open for the module to call it vibration.
Why one knock is many pulses
After a knock the spring keeps swinging for another 100–200 ms, and every time it opens you get a separate edge. Count edges and "one knock" turns into "twenty". The right way:
- An interrupt on every RISING edge — nothing is missed, not even half-millisecond pulses that a
digitalRead()inloop()would easily miss. - Group by a pause: an event is over when there has been no edge for 200 ms.
Code
// Knock detector: SW-420 + an interrupt, the rattle is grouped into single events
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() {
// a knock is a burst of edges; it is over after 200 ms of silence
if (edges > 0 && millis() - lastEdgeMs > 200) {
noInterrupts();
int n = edges;
edges = 0;
interrupts();
knocks++;
Serial.printf("Knock #%d (%d edges)\n", knocks, n);
digitalWrite(LED_PIN, HIGH);
delay(100);
digitalWrite(LED_PIN, LOW);
}
}
Click the sensor in the simulator — that is a "knock". The Serial Monitor shows how many edges one knock produced, and the counter goes up by one.
Why noInterrupts()
edges changes inside the interrupt. If the interrupt fires between reading edges and clearing it, one edge is lost. A short noInterrupts() … interrupts() block makes the two steps indivisible. On a dual-core ESP32 a portMUX_TYPE with portENTER_CRITICAL() is more robust, but for a knock counter this is enough.
Ideas
- A secret knock: remember the intervals between three to five knocks and open a lock (a servo) only for a similar rhythm, within ±25 %.
- A washing machine monitor: count events per minute. When the vibration has stopped for 5 minutes, the wash is done — send a Telegram message.
- On an Arduino Uno only D2 and D3 have interrupts — the simulator's Uno variant moves the sensor to D2 by itself.