Maker Pro
Maker Pro

Detecting A Knock Pattern

Jouellet

Feb 2, 2015
86
Joined
Feb 2, 2015
Messages
86
Hi everyone,

I'm looking for a way to detect a knock pattern. something like:

toc----toc---------------toc

Getting the 'toc' shouldn't be a big problem: I'm planning on using a microphone or a piezo, and detect 'spike' of signal, corresponding to the knock themselves.

I mostly use Arduino, but open to any other solution. Anyone has an idea on how to 'record' the pattern, and recognize it with a certain 'room' for similar pattern, as 2 knock sequences will never be perfectly the same.
 

Gryd3

Jun 25, 2014
4,098
Joined
Jun 25, 2014
Messages
4,098
Hi everyone,

I'm looking for a way to detect a knock pattern. something like:

toc----toc---------------toc

Getting the 'toc' shouldn't be a big problem: I'm planning on using a microphone or a piezo, and detect 'spike' of signal, corresponding to the knock themselves.

I mostly use Arduino, but open to any other solution. Anyone has an idea on how to 'record' the pattern, and recognize it with a certain 'room' for similar pattern, as 2 knock sequences will never be perfectly the same.
I can think of two ways immediately to do this.
Both require a hard-coded knock pattern, but I'm sure they can be adjusted to be dynamically programmed.
The first knock will have no variation, initialize a timer here. Subsequent knocks will trigger a comparison of the timer to the currently stored value, and if the timer is within range, allow it to proceed to the next knock, otherwise exit your 'listen' loop and show an error.
The narrower this variation, the more exact the knock will need to be. If the second knock time is set to 500ms , and your variation is set to 20ms, then the second knock should be accepted anywhere from 480ms to 520ms.

The other method is less 'timer' orientated, and will be designed more like a 'bit-mask' ...
A bit mask for your included knock pattern could be b10000101 .. The first knock would start the process of course. Each bit could represent 100ms for example, but this could be adjusted. After each 'time' unit, you can 'shift' this binary 'mask' and compare the bit on the end. If a knock happens while that bit is a 1, it's accepted. Otherwise you show an error and end you loop. You can use more than 8 bits for the mask and adjust timing based on the accuracy you want.

That is the two ideas I would try. But there is always more than one way to do things when it comes to code and design!
 

duke37

Jan 9, 2011
5,364
Joined
Jan 9, 2011
Messages
5,364
There are morse code receiving programs. I wrote one many years ago for an Acorn Atom. The more I sent, the more the program seemed to understand me.:)
 

wingnut

Aug 9, 2012
255
Joined
Aug 9, 2012
Messages
255
Hi everyone,

I'm looking for a way to detect a knock pattern. something like:

toc----toc---------------toc

Getting the 'toc' shouldn't be a big problem: I'm planning on using a microphone or a piezo, and detect 'spike' of signal, corresponding to the knock themselves.

I mostly use Arduino, but open to any other solution. Anyone has an idea on how to 'record' the pattern, and recognize it with a certain 'room' for similar pattern, as 2 knock sequences will never be perfectly the same.

I would try a frequency counter. There are plenty of free programs out there, including for Arduino, which are able to return a frequency of some regular repetitive motion, as a value in Hertz. Just do a Google search for "frequency counter" and "software". Through your computer mic. you should be able to detect the pattern. In your case patterns, since it has at least two frequencies.
 

Colin Mitchell

Aug 31, 2014
1,416
Joined
Aug 31, 2014
Messages
1,416
This circuit only turns ON when it detects 2 knocks:
KnockKnockDoorBell.gif
 

Jouellet

Feb 2, 2015
86
Joined
Feb 2, 2015
Messages
86
Hi Guys,

follow up on my project.

I did it by enabling the interrupt to capture when a knock is detected, then sampling every 25ms and saving the result.

I "teach" the sequence and for every position where a bit is recorded, I set the 2 bits before and after to '1', therefore making it a bit easier to get the same exact pattern when reading.

Every time a bit is expected and found, I keep doing a comparison. if I end my sampling sequence and found a '1' when one was expected, I call it a good code !

I first tried with a piezo, but I had to knock exactly where the piezo is. Result was not so impressive.

Ended up using a microphone as the pickup element.
 

hevans1944

Hop - AC8NS
Jun 21, 2012
4,878
Joined
Jun 21, 2012
Messages
4,878
So, your spiffy program can detect a "shave and a hair-cut... two bits" knock pattern? I'm impressed.

Next will be whistle codes I suppose, maybe followed by voice recognition... hmmm. How about recognizing tunes, like the theme from the movie Close Encounters of the Third Kind?
 
Last edited:

Jouellet

Feb 2, 2015
86
Joined
Feb 2, 2015
Messages
86
Hevans1944: Being francophone, I'm not sure what you mean by "shave and a hair-cut... two bits", but I guess it's positive !

As for whistle recognition, I like your optimism, but I don't think it's going to happen soon !

For those interested, here's the (almost) final code:
Code:
/*
* knock detector
* Jasmin Ouellet
* [email protected]
*
* pin 2 = Digital pulse from microphone board
* pin 4 : learn/run toggle switch
* pin 11: high/low sensitivity jumper
* pin 12: output connected to an Opto/relay
*
* Print to serial port enabled for debugging
*
* still need to save/recall bytes containing knock pattern to EEPROM
*/




#include <TimerOne.h>

bool Learning = 0;
bool Reading = 0;
bool RawData[65];
bool DataRead[65];
bool SavedData[65];
byte Data[12];
int  Pointer = 0;
int  Pos = 0;
bool GotOne = 0;
int  SetNextOne = 0;
bool KeepGoing = 0;
bool KnockDetected = 0;

void setup()
{
  attachInterrupt(0, StartAcquisition, RISING);
  pinMode(2, INPUT);
  pinMode(4, INPUT);
  pinMode(11, INPUT);  
  pinMode(12, OUTPUT);
  digitalWrite(12,LOW);
  Serial.begin(9600);
  Timer1.initialize(50000);                 // set a timer of length 50000 microseconds (or 0.05 sec)
  Timer1.attachInterrupt( timerIsr );       // attach the service routine here
  Serial.println("Erasing Data");           //Debug message
for (int i=1; i <= 63; i++)
    {
    RawData[i] = 0;                         // Erase everything
    DataRead[i] = 0;                        // Erase everything
    }
    Serial.println("Setup Done");
}
void loop()
{
if (Pointer == 64)                          // all sample acquired
  {
// what if we are in Learning mode
  if(Learning == 1)
    {
    Learning = 0;
    for (int w=0; w <= 7; w++)
      {
      Data[w] = 0;
      for (int i=0; i <= 7; i++)
        {
          Pos = (w*8)+i;
          Serial.print(RawData[Pos]);         // debug message
        }
      Serial.println("");
      }
    Pointer = 0;          
    BuildBytes();                             //Go transform bits into bytes
    }

// what if we are in Reading mode
  if(Reading == 1)
    {
    //Reading = 0;
    for (int w=0; w <= 7; w++)
      {
      Data[w] = 0;
      for (int i=0; i <= 7; i++)
        {
          Pos = (w*8)+i;
          Serial.print(RawData[Pos]);         // debug message
        }
      Serial.println("");                     // debug message
      }
      Serial.println("with deadband now !");  // debug message
    AddDeadband();                            // go add 1 level of deadband (bit before & Bit after)
if(digitalRead(11)==1)                        // if "High Sensibility" jumper in place, add another set of deadband
  {
  AddAnotherDeadband();
  }
    for (int w=0; w <= 7; w++)
      {
      for (int i=0; i <= 7; i++)
        {
          Pos = (w*8)+i;
          Serial.print(DataRead[Pos]);         // debug message
        }
      Serial.println("");                      // debug message
      }
    CompareData();                             // check if received data fit with learned bits + deadband
    }
Pointer = 0;
  }

}

void AddDeadband()
{
DataRead[0] = 1;
for (int i=1; i <= 63; i++)
    {
        if (RawData[i] == 0)
        {
            if(SetNextOne == 0)
            {
                if(RawData[i+1] == 1)
                {
                DataRead[i] = 1;
                SetNextOne = 1;
                }
                else
                {
                DataRead[i] = 0;
                }
            }
            else
            {
                DataRead[i] = 1;
                SetNextOne = 0;
            }
         }
        else
        {
        DataRead[i] = 1;  
        }
    }

}

void AddAnotherDeadband()
{
DataRead[0] = 1;
for (int i=1; i <= 63; i++)
    {
        if (DataRead[i] == 0)
        {
            if(SetNextOne == 0)
            {
                if(DataRead[i+1] == 1)
                {
                DataRead[i] = 1;
                SetNextOne = 1;
                }
                else
                {
                DataRead[i] = 0;
                }
            }
            else
            {
                DataRead[i] = 1;
                SetNextOne = 0;
            }
         }
        else
        {
        DataRead[i] = 1;  
        }
    }

}

void CompareData()
{

Serial.println("Begin Compare");             // debug message
Reading = 0;
KeepGoing = 1;
for (int i=1; i <= 63; i++)
    {
    if (SavedData[i] == 1)
      {
      if(DataRead[i] == 0)
        {
        Serial.println("oops !");             // debug message
        KeepGoing = 0;
        }
      }
     else
     {
     }
    }
if(KeepGoing ==1)
  {
Serial.println("Code the same");               // debug message
  digitalWrite(12,HIGH);                       // set output (relay) 'ON'
  delay(2500);
  digitalWrite(12,LOW);                        // set output (relay) 'OFF'
  }
}
void timerIsr()
{
if ((Learning == 1)||(Reading ==1))
  {
  if (KnockDetected == 1)
    {
    Serial.println("knock detected");           // debug message  
    if (GotOne == 0)
      {
      RawData[Pointer] = 1;
      GotOne = 1;    
      }
    else
      {
      RawData[Pointer] = 0;
      KnockDetected = 0;    
      }
    }
  if (KnockDetected == 0)
    {
    RawData[Pointer] = 0;
    GotOne = 0;
    }
  Pointer = Pointer + 1;
  }
KnockDetected = 0;
}


void BuildBytes()
{
  Pointer = 0;
  Serial.println("Ready to Build....");             // debug message
    for (int w=0; w <= 7; w++)
      {
      for (int i=0; i <= 7; i++)
        {
        if(RawData[Pointer]==1)
          {
          bitSet(Data[w], (7-i));
          SavedData[Pointer] = 1;
          }
        else
          {
          bitClear(Data[w], (7-i));        
          SavedData[Pointer] = 0;
          }
        Pointer = Pointer +1;
        }

      Serial.println(Data[w]);
      }
}

void StartAcquisition()
{
KnockDetected = 1;
if(digitalRead(4) == 0)
  {
  if(Learning == 0)
    {
    Learning = 1;
    Reading = 0;
    Serial.println("Beginning to learn.....");      // debug message
    }
  }

if(digitalRead(4) == 1)
  {
  if(Reading == 0)
    {
    Learning = 0;
    Reading = 1;
    Serial.println("Beginning to Read.....");       // debug message
    }
  }
}

I also included a picture of the final montage, with a 3D printed support.

J.
 

Attachments

  • IMG_3725.JPG
    IMG_3725.JPG
    54.5 KB · Views: 67

Jouellet

Feb 2, 2015
86
Joined
Feb 2, 2015
Messages
86
Okaaaaay !

I thought it was some kind of expression in English !

I actually was able to use that as my knock pattern !

Due to the closenest of some notes, I had to be a bit slower and could not use the "high sensitivity" on my circuit...
 
Top