Secret knock lock on an Arduino Uno
An SW-420 vibration sensor on the door listens for a secret rhythm, three quick knocks and one after a pause: the right one opens the servo bolt, anything else flashes red. The whole build, an Arduino Uno and 6 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.
Teaching? Assign it to your class in one click.
Intermediate About 25 minutes to build and run. Runs in your browser. Free, and no account needed.
The parts retail for about $34. On Mokxi they're free.
What you'll learn
- Detecting a knock with a vibration sensor
- Debouncing by ignoring input for 100 ms
- Comparing rhythm instead of exact timing
- Recording times in an array
- Moving a servo as a lock
- Difficulty: Intermediate
- Time: About 25 minutes
- Board: Arduino Uno R3
- Parts: 8
How it works
An SW-420 vibration sensor on pin 2 listens for knocks. The secret is a rhythm: knock, knock, knock, pause, knock. The sketch records the time of each knock and waits for 1.5 seconds of silence to know you are done.
Then it compares the gaps. Each gap is scaled so the longest is 100, and each must be within 25 of 50, 50, 100. A match opens the servo bolt and lights green for 4 seconds. A miss flashes the red LED three times.
Parts list
8 parts, plus the jumper wires. They retail for about $34; on Mokxi they're free.
- 1 × Arduino Uno R3In Starter Kit, Claw Machine Kit, Vending Machine Kit.
- 1 × Half breadboard
- 1 × Vibration sensor, SW-420
- 1 × Hobby servoIn Halloween Prop Kit, Christmas Lights Kit, Starter Projects Kit.
- 2 × Resistor, 220 ΩIn Starter Kit, Claw Machine Kit, Halloween Prop Kit.
- 1 × LED, greenIn Starter Kit, Halloween Prop Kit, Logic Lab Kit.
- 1 × LED, redIn Starter Kit, Halloween Prop Kit, Logic Lab Kit.
Wiring table
Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.
| Part and pin | Connects to |
|---|---|
| Hobby servo pin PWM | Arduino Uno R3 pin 9 |
| Resistor, 220 Ω (1) pin 1 | Arduino Uno R3 pin 7 |
| Resistor, 220 Ω (2) pin 1 | Arduino Uno R3 pin 6 |
| Vibration sensor, SW-420 pin DO | Arduino Uno R3 pin 2 |
| Vibration sensor, SW-420 pin VCC | Arduino Uno R3 pin 5V |
| Hobby servo pin VCC | Arduino Uno R3 pin 5V |
| Vibration sensor, SW-420 pin GND | Arduino Uno R3 pin GND |
| Hobby servo pin GND | Arduino Uno R3 pin GND |
| LED, green pin C | Arduino Uno R3 pin GND |
| LED, red pin C | Arduino Uno R3 pin GND |
| LED, green pin A | Resistor, 220 Ω (1) pin 2 |
| LED, red pin A | Resistor, 220 Ω (2) pin 2 |
The code
The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.
// Secret knock lock: an SW-420 vibration sensor taped to the door, a servo
// that slides the bolt, and two lamps.
//
// pin 2 the SW-420's DO (low when still, pulses high on a knock)
// pin 9 the servo's signal wire (its red wire on 5 V, brown on GND)
// pin 7 through 220 ohm to the green LED
// pin 6 through 220 ohm to the red LED
//
// The secret is a rhythm, not a count: knock, knock, knock, pause, knock. The
// gaps are short, short, long, the long one twice the others. Knocking faster
// or slower is fine, because the sketch compares the gaps with the longest
// one, not with a clock.
//
// # Hearing one knock
//
// A knock is not one clean pulse on DO. The switch inside the can bounces for
// a few tens of milliseconds, so DO pulses high several times. The sketch
// takes the first edge as the knock and ignores DO for 100 ms after it.
//
// # The rhythm
//
// After 1.5 s with no knock the sequence is over. Each gap is scaled so the
// longest is 100, and each has to be within 25 of the secret's.
#include <Servo.h>
const int SENSOR = 2;
const int BOLT = 9;
const int GREEN_LED = 7;
const int RED_LED = 6;
const int LOCKED = 0;
const int OPEN = 90;
const int MAX_KNOCKS = 10;
const unsigned long DEBOUNCE_MS = 100;
const unsigned long DONE_MS = 1500;
const int TOLERANCE = 25;
// The secret, as gaps scaled to the longest: short, short, long.
const int SECRET[] = {50, 50, 100};
const int SECRET_GAPS = sizeof(SECRET) / sizeof(SECRET[0]);
Servo bolt;
unsigned long knockAt[MAX_KNOCKS];
int knocks = 0;
void setup() {
Serial.begin(115200);
Serial.println("Mokxi Arduino Uno: secret knock lock");
pinMode(SENSOR, INPUT);
pinMode(GREEN_LED, OUTPUT);
pinMode(RED_LED, OUTPUT);
bolt.attach(BOLT);
bolt.write(LOCKED);
}
bool rhythmMatches() {
if (knocks - 1 != SECRET_GAPS) return false;
unsigned long longest = 1;
for (int i = 1; i < knocks; i++) {
unsigned long gap = knockAt[i] - knockAt[i - 1];
if (gap > longest) longest = gap;
}
for (int i = 1; i < knocks; i++) {
int scaled = (int)((knockAt[i] - knockAt[i - 1]) * 100 / longest);
Serial.print(" gap ");
Serial.print(scaled);
Serial.print(" (want ");
Serial.print(SECRET[i - 1]);
Serial.println(")");
int off = scaled - SECRET[i - 1];
if (off < -TOLERANCE || off > TOLERANCE) return false;
}
return true;
}
void unlock() {
Serial.println("unlocked");
digitalWrite(GREEN_LED, HIGH);
bolt.write(OPEN);
delay(4000);
bolt.write(LOCKED);
digitalWrite(GREEN_LED, LOW);
Serial.println("locked");
}
void refuse() {
Serial.println("wrong knock");
for (int i = 0; i < 3; i++) {
digitalWrite(RED_LED, HIGH);
delay(150);
digitalWrite(RED_LED, LOW);
delay(150);
}
}
void loop() {
unsigned long now = millis();
if (digitalRead(SENSOR) == HIGH) {
if (knocks == 0 || now - knockAt[knocks - 1] > DEBOUNCE_MS) {
if (knocks < MAX_KNOCKS) {
knockAt[knocks] = now;
knocks++;
Serial.print("knock ");
Serial.println(knocks);
}
}
}
if (knocks > 0 && now - knockAt[knocks - 1] > DONE_MS) {
if (rhythmMatches()) {
unlock();
} else {
refuse();
}
knocks = 0;
}
}
Code walkthrough
The key functions of the sketch, and what each one does.
setup sketch.ino
Sets the sensor and LED pins, attaches the servo and moves the bolt to the locked position at 0 degrees.
void setup() {
Serial.begin(115200);
Serial.println("Mokxi Arduino Uno: secret knock lock");
pinMode(SENSOR, INPUT);
pinMode(GREEN_LED, OUTPUT);
pinMode(RED_LED, OUTPUT);
bolt.attach(BOLT);
bolt.write(LOCKED);
}rhythmMatches sketch.ino
Checks there are exactly four knocks, finds the longest gap, and scales every gap against it. Each scaled gap must be within TOLERANCE of the secret, so knocking faster or slower still works.
bool rhythmMatches() {
if (knocks - 1 != SECRET_GAPS) return false;
unsigned long longest = 1;
for (int i = 1; i < knocks; i++) {
unsigned long gap = knockAt[i] - knockAt[i - 1];
if (gap > longest) longest = gap;
}
for (int i = 1; i < knocks; i++) {
int scaled = (int)((knockAt[i] - knockAt[i - 1]) * 100 / longest);
Serial.print(" gap ");
Serial.print(scaled);
Serial.print(" (want ");
Serial.print(SECRET[i - 1]);
Serial.println(")");
int off = scaled - SECRET[i - 1];
if (off < -TOLERANCE || off > TOLERANCE) return false;
}
return true;
}unlock sketch.ino
Prints a message, lights the green LED and turns the servo to OPEN. After 4 seconds it moves the bolt back and turns the LED off.
void unlock() {
Serial.println("unlocked");
digitalWrite(GREEN_LED, HIGH);
bolt.write(OPEN);
delay(4000);
bolt.write(LOCKED);
digitalWrite(GREEN_LED, LOW);
Serial.println("locked");
}loop sketch.ino
Notes each knock, ignoring any within DEBOUNCE_MS of the last. When 1.5 seconds pass with no knock, it calls rhythmMatches and then unlock or refuse, and starts over.
void loop() {
unsigned long now = millis();
if (digitalRead(SENSOR) == HIGH) {
if (knocks == 0 || now - knockAt[knocks - 1] > DEBOUNCE_MS) {
if (knocks < MAX_KNOCKS) {
knockAt[knocks] = now;
knocks++;
Serial.print("knock ");
Serial.println(knocks);
}
}
}
if (knocks > 0 && now - knockAt[knocks - 1] > DONE_MS) {
if (rhythmMatches()) {
unlock();
} else {
refuse();
}
knocks = 0;
}
}Troubleshooting
The usual mistakes with this circuit, and the fix for each.
- Every knock is refused This project
You need exactly four knocks, with the long pause before the last one. The Serial Monitor shows each scaled gap. - One knock counts as many This project
The switch bounces. The 100 ms ignore time handles it, so do not makeDEBOUNCE_MSsmaller. - The sequence never ends This project
The sketch waits 1.5 seconds after the last knock. Stop knocking and wait. - It reads the same all the time, or false triggers. A pot sets the comparator threshold and the sensor is a very sensitive switch. Vibration sensor
Turn the pot until the LED just goes off at rest, then a tap trips it. Read the digital output DO, and count one edge per knock instead of a level. - The output is wired to a 5 V logic level on a 3.3 V board, or power is missing. Vibration sensor
Power the module from 3.3 V or 5 V to match your board's logic level. Join VCC and GND, then connect DO to a digital input. Use an interrupt for fast knocks. - The servo jitters, twitches or resets the board when it moves. Hobby servo
Power it from its own 5 V supply, not the Arduino 5 V pin, and join the grounds. A hobby servo can pull several hundred milliamps. Add a 100 uF to 470 uF capacitor across its supply. - The servo hums at the end of its travel or will not reach full angle. Hobby servo
Stay inside the pulse range, about 1000 to 2000 us for most hobby servos. Pushing past the end stop makes it strain. Calibrate the min and max pulse for your servo. - The LED is dim, burns out, or the pin reads wrong because the resistor value is off. Resistor
Read the color bands again. A 220 ohm to 330 ohm resistor suits a typical LED on 5 V. 10k is a common pull-up or pull-down value. Check with a multimeter if unsure.
Change it and keep it
Open it in the editor, change the circuit or the code, and keep your version in a free account.