Introduction: KnockKnock - Open Your Door by Knocking

Es gibt heutzutage viele Arten von Authentifizierung. Während das Passwort am häufigsten verwendet wird, gab es schon in analogen Zeiten das Klopfmuster. Warum nicht Türen damit öffnen?


Wir haben uns vorgenommen ein Modul zu bauen, dass Klopfmuster an der Tür erkennt und die Tür bei Übereinstimmung öffnet, indem der Schlüssel umgedreht wird.

Supplies

  • Arduino
  • Motor - V-TEC 12V DC 6mm Antriebswelle Schneckengetriebemotor 100RPM
  • Control Board - Pololu DRV8876
  • Mikrofon (Sensor)
  • Case - 3D Print
  • Tür (inkl. Schloss)
  • Lautsprecher

Step 1: Klopfer Erkennen

Problem: Geringer Rausch-Signal-Abstand,

Lösung: Empfindlichkeit des Mikrofons erhöhen und feinjustieren


Wir haben zu dritt verschiedene Module getestet (z.B: Piezzoelement) zum erkennen von Klopfen und haben uns für das Mikrofonmodul entschieden. Wir haben den Analogausgang des Mikrofons verwendet, welcher 0-5V sendet, statt dem Digitalausgang, der ab einem bestimmten Schwellenwert 5v sendet, um die Werte auszulesen. Dadurch können wir in der Software flexibler mit dem Signal arbeiten und es dort feinjustieren. Die Empfindlichkeit des Signals konnte an dem Modul mit einer Schraube eingestellt werden.

Mit einem Rauschboden von ca. 2V und dem Klopfen dass bis 2.5V ging hatten wir eine Ausgangsposition um Klopfer zu erkennen. Je empfindlicher das Mikrofon eingestellt wurde, desto höher war der Rauschboden, aber auch der Rausch-Signal-Abstand. Die maximale Dauer eines Klopfers, also der Klangschwingungen eines Klopfers über dem Threshold, haben wir mit 50ms delay nach einer Erkennung festgelegt. Dadurch wurde ein Klopfer nicht als mehrere erkannt, man kann aber auch kein double-time mehr klopfen.


Diese Logik haben wir in die Funktion detectKnock geschrieben, welche in einer While-Schleife wartet und entweder den Zeitstempel des erkannten Klopfers in ms zurückgibt oder nach 5 Sekunden abbricht und das mit der Rückgabe von -1 angibt.

long detectKnock(){
  long start = millis();
  while((analogRead(inputPinMic) * (5.0 / 1023.0)) <= thresh){
    if(millis() - start >= 5000){
      return -1;
    }
  }
  return millis();
}

Step 2: Klopfmuster Erkennen

Problem: Klopfmuster geht bei Neustart oder Stromabbruch verloren

Lösung: Klopfmuster im nichtflüchtigen EEPROM speichern

Problem: Wurde das eingegebene Klopfmuster richtig erkannt?

Lösung: Ausgabe des gespeicherten Musters mit LED bzw. Lautsprecher nach dem einspeichern


Wir haben Klopfmuster als Zeit in ms zwischen Klopfern in einem Array gespeichert. Um Klopfmuster abzugleichen haben wir die jeweilige Zeit zwischen den Eingabeklopfern mit den gespeicherten Zeiten verglichen und jeweils eine feste Fehlerzeit zugelassen. Meine Idee, es zu ermöglichen Klopfmuster in verschiedenen Geschwindigkeiten zu klopfen, indem ein Skalierungsfaktor erkannt und auf die Delays mulitpliziert wird, haben wir nicht umgesetzt.


Diese Logik haben wir in die Funktion writePattern bzw. matchPattern geschrieben.


writePattern speichert die Zeiten in ms von detectKnock als Array in den EEPROM-Speicher. Dabei werden einfach die Keys ab 0 aufwärts verwendet. Ein Klopfer als int wird dabei in zwei bytes aufgeteilt und belegt damit zwei Keys. Mit 1024 bytes Speicherplatz könnten somit theoretisch 512 Klopfer gespeichert werden. Um das Ende eines Klopfmusters zu markieren, wird am Schluss zwei Bytes mit dem Wert 255 abgespeichert.

void writePattern(){
  Serial.println("Writing Pattern now");
  long lastStep = detectKnock();
  delay(50);

  if(lastStep == -1){
    Serial.println("No knock detected resuming with old one");
    return;
  }

  long currentTime;
  int i = 0;

  //detect knock at the end of loop to wait for the next
  while((currentTime = detectKnock()) != -1){
    delay(50);
    int difference = currentTime - lastStep;
    byte second = (byte) (difference & 0xFF);
    byte first = (byte) ((difference >> 8) & 0xFF);

    Serial.print(first);
    Serial.print(" ");
    Serial.println(second);

    EEPROM.write(i++, first);
    EEPROM.write(i++, second);

    //TODO vielleicht ein delay hinzufügen
    lastStep = currentTime;
  }

  EEPROM.write(i++, 255);
  EEPROM.write(i, 255);
  Serial.println("Finished Pattern now");

Um zu überprüfen, ob das Klopfmuster richtig erkannt wurde, wird es noch einmal ausgegeben über ein LED bzw. einen Lautsprecher.

  i = 0;
  int allowedDifference = 0;
  byte first;
  byte second;

  do{
    delay(allowedDifference);

    analogWrite(ledPin, 60);
    delay(50);
    analogWrite(ledPin, 0);

    first = EEPROM.read(i++);
    second = EEPROM.read(i++);

    allowedDifference = 0;
    allowedDifference |= first;                  // Set the high byte
    allowedDifference = allowedDifference << 8;  // Shift the high byte to the left by 8 bits
    allowedDifference |= second;

  }while(first != 255);

} //writePattern end


matchPattern list diese gespeicherten Zeiten aus und vergleicht sie jeweils mit der letzten Zeit aus detectKnock abzüglich Fehlertoleranz. Falls die Zeit in der Toleranz liegt, wir die Schleife wiederholt, sodass entweder wegen einer falschen Zeit false zurückgegeben wird oder die zwei Bytes mit Wert 255 gelesen werden und true zurückgegeben wird.

int matchPattern(){
  long lastStep = millis();
  delay(50);

  long currentTime;
  int i = 0;
  int tolerance = 70;

  byte first = EEPROM.read(i++);
  byte second = EEPROM.read(i++);

  while((currentTime = detectKnock()) != -1){
    int allowedDifference = 0;

    allowedDifference |= first;                  // Set the high byte
    allowedDifference = allowedDifference << 8;  // Shift the high byte to the left by 8 bits
    allowedDifference |= second;

    Serial.print((currentTime - lastStep));
    Serial.print(" ");
    Serial.println(allowedDifference);

    if((currentTime - lastStep) <= (allowedDifference - tolerance) ||
      (currentTime - lastStep) >= (allowedDifference + tolerance)){
        return 0;
    }

    lastStep = currentTime;
    first = EEPROM.read(i++);
    second = EEPROM.read(i++);

    if((first == 255) && (second == 255)){
      return 1;
    }
    delay(50);
  }
}


Code auf Github

Step 3: Schlüssel Umdrehen

Problem: Wann ist die Tür auf?

Lösung: Verwendung des Control Boards um hohen Stromfluss zu erkennen.

Problem: Wie kann die Tür abgeschlossen werden?

Lösung: Wäre freidrehender Motor und Schloss mit der Möglichkeit auf beiden Seiten ein Schlüssel reinzustecken (hatten wir nicht). Oder Motorsansteuerung von außerhalb. Abschließen von innen (Sichtkontakt zur Tür) mit Fernbedienung ist möglich.


Um die Tür bei übereinstimmenden Klopfmustern dann auch zu öffnen, haben wir einen Motor entsprechend programmiert und mit einem Haken in den Schlüssel im Schloss eingehakt. Über ein Control Board konnten wir erkennen wann der Motor gegen einen Widerstand stieß und nicht mehr weiter drehte um daraus zu schließen, dass das Schloss offen ist.

Step 4: Zusammenbauen

An einer Testtür haben wir schließlich alle verlöteten Komponente angebracht in einem 3D-gedruckten Case.

Mit einem Knopf kann die Musteraufnahme gestartet werden, zwei LEDs zeigen mit rot und grün an, ob ein Muster richtig oder falsch war und ein Lautsprecher gibt, zusätzlich zu weiteren Sounds, noch das eingespeicherte Muster einmal aus.

Step 5: Anwendungsablauf

Zuerst kann nach dem drücken des Knopfes ein Klopfmuster eingespeichert werden. Durch die Lautsprecherausgabe kann man erkennen, ob das Muster richtig aufgenommen wurde.

Daraufhin kann man aus der Tür gehen und sie schließen. (Abschließen funktioniert noch nicht)

Wenn man wieder eintreten will, kann man mit dem vorher gespeicherten Muster an die Tür klopfen und sie öffnet sich automatisch.