How do i get the knock lock to keep stored knock after reset?

Hello. I was wondering if there is a way to pre program the knock pattern for the knock lock so I don't have to redo the knock pattern everytime the arduino resets?

My example sketch is based off this video (https://www.youtube.com/watch?v=meKv4BFXXto). It works I just need to know how to make the knock pattern "permanent".

Thanks in advance for the help.

//Viral Science www.youtube.com/c/viralscience  www.viralsciencecreativity.com
//Secret Knock Pattern Door Lock

const int knockSensor = 0;
const int programSwitch = 2;
const int lockMotor = 3;
const int redLED = 4;
const int greenLED = 5;
 
const int threshold =100;
const int rejectValue = 200;
const int averageRejectValue = 15;
const int knockFadeTime = 150;
const int lockTurnTime = 2000;

const int maximumKnocks = 20;
const int knockComplete = 1200;

int secretCode[maximumKnocks] = {50, 25, 25, 50, 100, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int knockReadings[maximumKnocks];
int knockSensorValue = 0;
int programButtonPressed = false;

void setup() {
  pinMode(lockMotor, OUTPUT);
  pinMode(redLED, OUTPUT);
  pinMode(greenLED, OUTPUT);
  pinMode(programSwitch, INPUT_PULLUP);
  
  Serial.begin(9600);
  Serial.println("Program start.");
  
  digitalWrite(greenLED, HIGH);
}

void loop() {
  knockSensorValue = analogRead(knockSensor);
  
  if (digitalRead(programSwitch)==LOW){
    programButtonPressed = true;
    digitalWrite(redLED, HIGH);
  } else {
    programButtonPressed = false;
    digitalWrite(redLED, LOW);
  }
  
  if (knockSensorValue >=threshold){
    listenToSecretKnock();
  }
} 

void listenToSecretKnock(){
  Serial.println("knock starting");   

  int i = 0;
  for (i=0;i<maximumKnocks;i++){
    knockReadings[i]=0;
  }
  
  int currentKnockNumber=0;
  int startTime=millis();
  int now=millis();
  
  digitalWrite(greenLED, LOW);
  if (programButtonPressed==true){
     digitalWrite(redLED, LOW);
  }
  delay(knockFadeTime);
  digitalWrite(greenLED, HIGH);  
  if (programButtonPressed==true){
     digitalWrite(redLED, HIGH);                        
  }
  do {
    knockSensorValue = analogRead(knockSensor);
    if (knockSensorValue >=threshold){
      Serial.println("knock.");
      now=millis();
      knockReadings[currentKnockNumber] = now-startTime;
      currentKnockNumber ++;
      startTime=now;          
      digitalWrite(greenLED, LOW);  
      if (programButtonPressed==true){
        digitalWrite(redLED, LOW);
      }
      delay(knockFadeTime);
      digitalWrite(greenLED, HIGH);
      if (programButtonPressed==true){
        digitalWrite(redLED, HIGH);                         
      }
    }

    now=millis();
    
  } while ((now-startTime < knockComplete) && (currentKnockNumber < maximumKnocks));
  
  if (programButtonPressed==false){
    if (validateKnock() == true){
      triggerDoorUnlock(); 
    } else {
      Serial.println("Secret knock failed.");
      digitalWrite(greenLED, LOW);
      for (i=0;i<4;i++){          
        digitalWrite(redLED, HIGH);
        delay(100);
        digitalWrite(redLED, LOW);
        delay(100);
      }
      digitalWrite(greenLED, HIGH);
    }
  } else {
    validateKnock();
    Serial.println("New lock stored.");
    digitalWrite(redLED, LOW);
    digitalWrite(greenLED, HIGH);
    for (i=0;i<3;i++){
      delay(100);
      digitalWrite(redLED, HIGH);
      digitalWrite(greenLED, LOW);
      delay(100);
      digitalWrite(redLED, LOW);
      digitalWrite(greenLED, HIGH);      
    }
  }
}


void triggerDoorUnlock(){
  Serial.println("Door unlocked!");
  int i=0;
  
  digitalWrite(lockMotor, HIGH);
  digitalWrite(greenLED, HIGH);
  
  delay (lockTurnTime);
  
  digitalWrite(lockMotor, LOW);
  
  for (i=0; i < 5; i++){   
      digitalWrite(greenLED, LOW);
      delay(100);
      digitalWrite(greenLED, HIGH);
      delay(100);
  }
   
}

boolean validateKnock(){
  int i=0;
 
  int currentKnockCount = 0;
  int secretKnockCount = 0;
  int maxKnockInterval = 0;
  
  for (i=0;i<maximumKnocks;i++){
    if (knockReadings[i] > 0){
      currentKnockCount++;
    }
    if (secretCode[i] > 0){
      secretKnockCount++;
    }
    
    if (knockReadings[i] > maxKnockInterval){
      maxKnockInterval = knockReadings[i];
    }
  }
  
  if (programButtonPressed==true){
      for (i=0;i<maximumKnocks;i++){
        secretCode[i]= map(knockReadings[i],0, maxKnockInterval, 0, 100); 
      }
      digitalWrite(greenLED, LOW);
      digitalWrite(redLED, LOW);
      delay(1000);
      digitalWrite(greenLED, HIGH);
      digitalWrite(redLED, HIGH);
      delay(50);
      for (i = 0; i < maximumKnocks ; i++){
        digitalWrite(greenLED, LOW);
        digitalWrite(redLED, LOW);  
        if (secretCode[i] > 0){                                   
          delay( map(secretCode[i],0, 100, 0, maxKnockInterval));
          digitalWrite(greenLED, HIGH);
          digitalWrite(redLED, HIGH);
        }
        delay(50);
      }
    return false;
  }
  
  if (currentKnockCount != secretKnockCount){
    return false; 
  }
  int totaltimeDifferences=0;
  int timeDiff=0;
  for (i=0;i<maximumKnocks;i++){
    knockReadings[i]= map(knockReadings[i],0, maxKnockInterval, 0, 100);      
    timeDiff = abs(knockReadings[i]-secretCode[i]);
    if (timeDiff > rejectValue){
      return false;
    }
    totaltimeDifferences += timeDiff;
  }
  if (totaltimeDifferences/secretKnockCount>averageRejectValue){
    return false; 
  }
  
  return true;
  
}

Where in that code is your knock pattern entered?

The code is entered after the button is pressed. Store knock count and interval.

Maybe the EEPROM for storing data which is persistent across sessions.

That is not the way you deal with millis values, an int is way too small. You need an unsigned long variable type when using the millis timer.

Corrected, thanks.

I'll have to look into this thanks. I've had some success checking for a certain number of knocks withing a given timeframe (say 5 knocks in 1 sec). I'm thinking of a way to expand on that and maybe that will get me what I need

When the button (programSwitch) is pressed it begins to "listen for secret knock" then validates that knock against the stored knock in "ValidateKnock)

It stores the secret knock.

So, are you wanting to store each listened-to knock that is entered? That is what I make of your description. How many of these will you store and when will they ever be removed?

No. I am trying to pre program the secret knock (like in the source code) so when the arduino is power cycled the secret knock is already there. With the program as is I have to "retrain" the secret knock everytime the arduino shuts down. There will be only 1 secret knock and will be removed if you alter it in the source code

For those who are curious I made a different version that allows me to set the knock pattern within the source code using timers and if statements to validate if the knock pattern is correct.

This code is raw but it works for the puzzle game I am building for my little ones. You can play around with the intervals and number of knocks to make your own custom pattern.

const int buttonPin = 2;  // the number of the pushbutton pin
int buttonState = 0;  // variable for reading the pushbutton status

const int piezo = A0;     // pin the piezo is attached to
const int green = 5;  // pin the green LED is attached to
const int red = 4;   // pin the red LED is attached to

int knockVal;   // variable for the piezo value

const int quietKnock = 100;   // variables for the high and low limits of the knock value
const int loudKnock = 200;    // variables for the high and low limits of the knock value
int numberOfKnocks1 = 0;   // variable for how many valid knocks you've received in sequence 1
int numberOfKnocks2 = 0;   // variable for how many valid knocks you've received in sequence 2
int numberOfKnocks3 = 0;   // variable for how many valid knocks you've received in sequence 3

int correctNumberofKnocks1 = 3;   // set the number of knocks for sequence 1
int correctNumberofKnocks2 = 2;   // set the number of knocks for sequence 2
int correctNumberofKnocks3 = 4;   // set the number of knocks for sequence 3

int maxKnocks = 10;   // set max umber of knocks to listen for. helps prevent tricking the system with continous knocks

const long interval1 = 1000;  // interval to listen for knock sequence 1
const long interval2 = 500;  // interval to listen for knock sequence 2
const long interval3 = 1000;  // interval to listen for knock sequence 3
const long knockComplete = 3000;   //time to complete secret knock sequence (good rule of thumb to be 500 ms longer than all intervals)
const long knockFadeTime = 250;  //buffer between sequences to help prevent tricking the system with continous knocks

void setup() 
{
  Serial.begin(9600);
  pinMode(green, OUTPUT);
  pinMode(red, OUTPUT);
  pinMode(buttonPin, INPUT_PULLUP);
}

void loop() 
{
  buttonState = digitalRead(buttonPin);

  if (buttonState == LOW)
  {
    digitalWrite(red, HIGH);
    unsigned long startTime = millis();

    while (millis() - startTime < knockComplete)
    {
      //************ Knock Pattern 1 ************
      unsigned long startTime1 = millis();
      while (millis() - startTime1 < interval1)
      {
        knockVal = analogRead(piezo);
        if (numberOfKnocks1 < maxKnocks && knockVal > quietKnock && knockVal < loudKnock)
        {
          numberOfKnocks1++;
          digitalWrite(green, HIGH);
          delay(100);
          digitalWrite(green, LOW);
        }
      }
      //************ Knock Pattern 2 ************
      delay(knockFadeTime);
      unsigned long startTime2 = millis();
      while (millis() - startTime2 < interval2)
      {
        knockVal = analogRead(piezo);
        if (numberOfKnocks2 < maxKnocks && knockVal > quietKnock && knockVal < loudKnock)
        {
          numberOfKnocks2++;
          digitalWrite(green, HIGH);
          delay(100);
          digitalWrite(green, LOW);
        }
      }
      //************ Knock Pattern 3 ************
      delay(knockFadeTime);
      unsigned long startTime3 = millis();
      while (millis() - startTime3 < interval3)
      {
        knockVal = analogRead(piezo);
        if (numberOfKnocks3 < maxKnocks && knockVal > quietKnock && knockVal < loudKnock)
        {
          numberOfKnocks3++;
          digitalWrite(green, HIGH);
          delay(100);
          digitalWrite(green, LOW);
        }
      }
    
    }
    if (numberOfKnocks1 == correctNumberofKnocks1 && numberOfKnocks2 == correctNumberofKnocks2 && numberOfKnocks3 == correctNumberofKnocks3) 
    {
      digitalWrite(green, HIGH);
      digitalWrite(red, LOW);
      Serial.println("You're In!");
      delay(1000);
      digitalWrite(green, LOW);
      numberOfKnocks1 = 0;
      numberOfKnocks2 = 0;
      numberOfKnocks3 = 0;
    }
    else
    {
      digitalWrite(red, LOW);
      delay(50);
      digitalWrite(red, HIGH);
      delay(50);
      digitalWrite(red, LOW);
      delay(50);
      digitalWrite(red, HIGH);
      delay(50);
      digitalWrite(red, LOW);
      delay(50);
      digitalWrite(red, HIGH);
      delay(50);
      digitalWrite(red, LOW);
      Serial.println("Incorrect Sequence");
      numberOfKnocks1 = 0;
      numberOfKnocks2 = 0;
      numberOfKnocks3 = 0;
    }
  }
}

These four pairs of code will always be true... how are they making your code work (compare stored data)?

Hey. They are only true for the duration of the countdown timer (intervals).

For this example I have 3 sequences of knocks that I use for my knock pattern. Each knock sequence is given a time that it has to be completed (for example 3 knocks in 500ms). I have those labeled as Interval1, Interval2, Interval3. They I have another variable that limits the time to complete the 3 knock sequences (knockComplete).

For the while loops I first store a startTime variable then subtract that variable from the current millis() and that is done while the recorded time is less than the preset interval.

Within each while loop I am capturing the number of knocks heard. Then in the If statement I am saying if the number of knocks heard is equal to the correctNumberofKnocks the pattern is deemed correct.

So far it's good for what I need it for but I'm sure it can be made more efficient

Give he first line any value... anyMilliseconds

The second line will be executed in 1.25 nanoseconds after the end of the first line (there are 1000000 nanoseconds in one millisecond), with a calculation of (anyMillis - anyMillis + 1.25 nanos < interval) will be true for every test of an interval greater than 1.25 nanoseconds.