Open a secret box with a knock code

Knock a secret rhythm on the lid and a servo unlocks the box. Knock the wrong rhythm and it stays shut. The knock sensor listens and the UNO compares the timing.
  • Level:Intermediate
  • Duration: 1 h
  • Ages 12+
  • Platform: Arduino

Tools for this lesson

Helpful for this build. Open a tool to see tips and more projects that use it.

Open a secret box with a knock code – EEMENTOR STEM™ lesson

Secret clubs have secret knocks. In this project, so does your box. The knock sensor listens to the lid, and the UNO records the rhythm of your knocks: not just how many, but the gaps between them. If the rhythm matches the secret one, a servo pulls the latch back and the green LED lights. The secret knock is the famous "shave and a haircut … two bits".

What you'll learn

  • How to record the time between events
  • How to compare a rhythm when the speed is different every time
  • How to allow for small mistakes with a tolerance
  • How to combine a sensor, a servo and LEDs into one device

What you need

  • UNO R3 board and USB cable (UNO R3 Starter Kit)
  • Knock sensor module (45-in-1 Sensor Kit)
  • Servo motor (UNO R3 Starter Kit)
  • Green LED, red LED and 2 resistors, 220 Ω (UNO R3 Starter Kit)
  • A small cardboard box with a lid, and tape
  • Breadboard and jumper wires (UNO R3 Starter Kit)

Make it real: tape the knock sensor firmly to the inside of the lid. Tape the servo inside the box so that its horn swings in front of a strip of card glued to the lid: horn across the strip = locked, horn turned away = open.

Wiring

Breadboard wiring diagram: Open a secret box with a knock code
Breadboard view. Check each connection against the wiring table below.
From To
Knock sensor S UNO pin 2
Knock sensor + and − 5V and GND
Servo orange / yellow wire UNO pin 9
Servo red wire 5V
Servo brown / black wire GND
UNO pin 5 220 Ω resistor, then the green LED's long leg
UNO pin 6 220 Ω resistor, then the red LED's long leg
Both LED short legs GND

Sensor modules from different makers can label and order their pins differently. Always follow the labels printed on your module.

The secret knock

"Shave and a haircut … two bits" is seven knocks: knock, knock-knock, knock, knock, (pause) knock, knock. The sketch stores the gaps between the knocks as numbers from 0 to 100, where 100 is the longest gap. That way it doesn't matter whether you knock fast or slowly, only that the rhythm is right.

The sketch

#include <Servo.h>

const int KNOCK_PIN = 2;
const int SERVO_PIN = 9;
const int GREEN_LED = 5;
const int RED_LED = 6;

const int MAX_KNOCKS = 10;
// Gaps of "shave and a haircut ... two bits", 100 = the longest gap
const int SECRET[MAX_KNOCKS] = {50, 25, 25, 50, 100, 50, 0, 0, 0, 0};
const int TOLERANCE = 25;          // how far each gap may be off
const int LOCKED = 0, OPEN = 90;   // servo angles of the latch

Servo latch;
int knockRest;

void setLatch(int angle) {
  latch.attach(SERVO_PIN);
  latch.write(angle);
  delay(500);
  latch.detach();                  // no buzzing while it waits
}

bool knock() {
  return digitalRead(KNOCK_PIN) != knockRest;
}

// Records knocks until there is a pause of 1.2 s. Returns the number of gaps.
int listen(unsigned long gaps[]) {
  int count = 0;
  unsigned long last = millis();
  delay(80);                                     // the first knock is still ringing
  while (millis() - last < 1200 && count < MAX_KNOCKS) {
    if (knock()) {
      unsigned long now = millis();
      gaps[count++] = now - last;
      last = now;
      digitalWrite(RED_LED, HIGH);              // flash with every knock
      delay(80);
      digitalWrite(RED_LED, LOW);
    }
  }
  return count;
}

bool matches(unsigned long gaps[], int count) {
  int secretCount = 0;
  while (secretCount < MAX_KNOCKS && SECRET[secretCount] > 0) secretCount++;
  if (count != secretCount) return false;

  unsigned long longest = 1;
  for (int i = 0; i < count; i++) longest = max(longest, gaps[i]);
  for (int i = 0; i < count; i++) {
    int scaled = gaps[i] * 100 / longest;        // same scale as SECRET
    if (abs(scaled - SECRET[i]) > TOLERANCE) return false;
  }
  return true;
}

void setup() {
  Serial.begin(9600);
  pinMode(KNOCK_PIN, INPUT);
  pinMode(GREEN_LED, OUTPUT);
  pinMode(RED_LED, OUTPUT);
  knockRest = digitalRead(KNOCK_PIN);
  setLatch(LOCKED);
  Serial.println("Knock the secret code...");
}

void loop() {
  if (!knock()) return;                          // wait for the first knock

  unsigned long gaps[MAX_KNOCKS];
  int count = listen(gaps);
  Serial.print("Heard ");
  Serial.print(count + 1);
  Serial.println(" knocks");

  if (matches(gaps, count)) {
    Serial.println("Correct! Opening for 8 seconds");
    digitalWrite(GREEN_LED, HIGH);
    setLatch(OPEN);
    delay(8000);
    setLatch(LOCKED);
    digitalWrite(GREEN_LED, LOW);
  } else {
    Serial.println("Wrong knock");
    for (int i = 0; i < 3; i++) {
      digitalWrite(RED_LED, HIGH);
      delay(150);
      digitalWrite(RED_LED, LOW);
      delay(150);
    }
  }
}

How it works

When the first knock arrives, listen() notes the time and waits for more. For every knock it stores the gap since the one before, until you pause for more than 1.2 seconds. Seven knocks give six gaps.

People never knock at exactly the same speed, so the sketch compares the rhythm, not the times. It finds the longest gap, scales every gap so that the longest becomes 100, and checks each one against the secret within a tolerance of 25. A fast and a slow version of the same rhythm give almost the same numbers. The number of knocks must match exactly.

The knock sensor contains a tiny spring that touches a contact when it is shaken, so one knock can produce several short pulses. Waiting 80 ms after each knock lets it settle before the next one can count. The latch servo is only powered while it moves, so it doesn't hum while the box waits.

Try this next

  • Change SECRET to your own rhythm. Print the scaled gaps in the Serial Monitor to see what your knock looks like.
  • Add a "learn" mode: hold a button while knocking to store a new secret.
  • Lock the box for a minute after three wrong knocks in a row.

Troubleshooting

  • Knocks are not heard: the sensor must be firmly taped to the lid. Knock close to it, with your knuckle.
  • It hears more knocks than you made: raise both delay(80) lines to 120.
  • The right knock doesn't open the box: open the Serial Monitor to see how many knocks were heard, and raise TOLERANCE to 30.
  • The board restarts when the servo moves: servos draw bursts of current. Use a good USB port or a powered hub, and check that the servo's red wire is on 5V.

Build it yourself

Everything used in this lesson comes in one kit, with parts tested by our engineers.

UNO R3 Starter Kit | Arduino IDE Compatible | Free Tutorials $56.00

Shop the kit