This package allows you to record a knock pattern, saving it on the EEPROM and testing it on the fly. So you can react on a specific knock pattern. All that happens asynchronous and with many settings!
Download the project and extract it into the library folder of Arduino. Under Linux: ~/Arduino/libraries
In the IDE you can use Tools > Library Manager... (or press ctrl+shift+I) and then search for "KnockPatternDetector". Install it. Finished.
/*
KnockPatternDetector
Checks on pin 2 for a signal (e.g. use a button).
First of all you need to open the console (ctrl+shift+m)
and send "record". Then you can 'knock' a pattern (on the button).
After waiting one second it will confirm you that it was recorded and saved.
Now you can 'knock' that pattern again at any time and check the console.
If you made a mistake during knocking you also need to wait one second.
*/
#include <KnockPatternDetector.h>
const byte PIN_KNOCK_SENSOR = 2;
const byte EEPROM_PATTERN = 0;
const byte MAXIMUM_ERROR = 25;
KnockDigitalHandler handler = KnockDigitalHandler(PIN_KNOCK_SENSOR);
KnockPattern pattern = KnockPattern::load(EEPROM_PATTERN);
KnockRecorder recorder(&handler);
void setup() {
Serial.begin(9600);
}
void loop() {
recorder.handle();
if (!recorder.recording()) {
byte error = pattern.test(recorder.getPattern());
if (error < MAXIMUM_ERROR) {
Serial.println("that was right!");
} else {
Serial.println("incorrect");
}
recorder.restart();
}
if (Serial.available()) {
String command = Serial.readStringUntil('\n');
if (command == "record") {
recorder.restart();
Serial.println("recording...");
while ((bool) recorder.handle()) {}
pattern = recorder.getPattern();
pattern.save(0);
recorder.restart();
Serial.println("recorded!");
} else {
Serial.println("unknown command");
}
}
}
It's not finished :)
This is an interface that detects a knock. For example the KnockDigitalHandler detects a 'knock' when you press a button. So every time you press the button, it will recognise it as a knock. The negate variable.
An interface (abstract class) that gives you the ability to detect a knock by your own sensor (maybe a complicated one) To do that you need to override the method:
bool checkKnock();
this will be called whenever a recorder wants know, if someone triggered your sensor. Return true if there was a knock, return false if not. An example for KnockDigitalHandler: Header CPP
KnockDigitalHandler(byte pin, bool negate = true);
- pin: the pin where the button is connected with the arduino
- negate (defaults to true): if it's false it will handle a HIGH-Signal as knock, on true it handels a LOW-Signal as knock
Note: You need to set the pinMode by yourself
KnockAnalogHandler(byte pin, unsigned short threshold, bool lower = false);
- pin: the pin where the button is connected with the arduino
- threshold: the analog value of the pin when it should lead to a knock
- lower (defaults to false): if the analog value needs to be lower than the threshold or above it.
Note: You need to set the pinMode by yourself
This class records your knock pattern. First of all you need to pass a KnockHandler so it knows when a knock happens.
Every loop you need to call the method recorder.handle()
so that it can detect your knocks. After reaching the timeout (default 1000ms), it stops recording. You can check that using:
if (recorder.recording()) { }
Then you can create your KnockPattern out of that using
KnockPattern pattern = recorder.getPattern();
Note: it won't reach the timeout until the first knock happens
KnockRecorder(KnockHandler* handler, unsigned int timeout = 1000, unsigned short snap = 64);
- handler: the KnockHandler that detects a knock
- timeout (defaults to 1000ms): the time the recorder will wait, until he pauses the recording
- snap (defaults to 64 per second): the recorder rounds the
knockTiming / (1000 / snap)
. The lower the value, the lower the precision. You can reduce the value if you have problems with a to high precision.
The recorder has some public variables for configuration:
- handler (set by constructor): the KnockHandler that detects a knock
- timeout (set by constructor): the time the recorder will wait, until he pauses the recording
- snap (set by constructor): the recorder rounds the
knockTiming / (1000 / snap)
. The lower the value, the lower the precision. You can reduce the value if you have problems with a to high precision. - debounce (defaults to 50ms): the delay it waits after a new knock can be detected
- max (defaults to 20): after the recorder records more than the max value it automatically stops recording
KnockPattern getPattern();
Generates a KnockPattern out of the recorded data and returns it.
void restart();
Clears all recorded data and starts a new recording
KnockStatus handle();
Needs to be called every loop, so that the recorder can work. Pretty important. It returns the current status of the recorder:
enum class KnockStatus : byte {
NotRecording, Recording, Knock
};
- NotRecording: The recorder does nothing. Because it's not recording
- Recording: The recorder is recording, but there was no knock detected
- Knock: It's recording and a knock was detected. You can use this for a status LED
bool recording();
Returns if the recorder is currently recording
void stop();
Stops the recorder
void continueRecord();
TODO: name should be changed
If the recorder was paused by stop()
you can continue with this method.
bool empty();
Returns if it currently recorded nothing.
void clear();
Deletes all recorded data.
Note: It won't stop recording.
A simple list of the knocks (timings in percent, as bytes). You can use the method test to see if another pattern matches it. The method returns an error (0-255), 0 is perfect and 255 is very bad:
// recordedPattern and pattern is somewhere defined (type: KnockPattern)
if (recordedPattern.test(pattern) < 25) {
// matches
}
You can also save and load a pattern from the EEPROM using pattern.load(0)
and pattern.save(0)
KnockPattern();
KnockPattern(std::vector<byte> timings);
- timings: the knocks in percentage that describe the pattern
static KnockPattern load(unsigned int address);
Loads a pattern from the EEPROM beginning at address.
void save(unsigned int address);
Saves the pattern at the address in EEPROM.
byte test(KnockPattern pattern);
Tests how equal the both patterns are. Returns 0 if it was perfect, 255 if it wasn't similar at all Algorithm
- If the count of knocks is different it will return error 255 (100%)
- Else it finds the maximum difference between two knock timings