/*
* Secret Knock Door Lock - TinyML Principles
* Pattern: tap-PAUSE-tap-tap (PAUSE-QUICK-PAUSE)
*
* TinyML Concepts Demonstrated:
* - Feature Extraction (timing gaps)
* - Normalization (0-1 scaling)
* - Pattern Classification (inference)
* - Confidence Scoring
*/
// ============================================
// Pin Definitions
// ============================================
const int BUTTON_PIN = 15;
const int LED_PIN = 2;
// ============================================
// Pattern Parameters
// ============================================
const int MAX_TAPS = 4;
const int MAX_GAPS = 3;
const unsigned long PATTERN_TIMEOUT = 3000;
const unsigned long DEBOUNCE_DELAY = 50;
// ============================================
// Model Parameters (Simulated Neural Network)
// ============================================
const float MODEL_THRESHOLD = 0.5; // Classification threshold
const int MODEL_SIZE_BYTES = 2376; // Simulated model size
// Neural network weights (simulated - renamed to avoid conflicts)
const float weight1[3] = {2.5, -3.2, 2.8}; // Layer 1 weights
const float bias1 = -0.5; // Layer 1 bias
const float weight2 = 1.8; // Output weight
const float bias2 = 0.2; // Output bias
// ============================================
// Global Variables
// ============================================
unsigned long tapTimes[MAX_TAPS];
unsigned long gaps[MAX_GAPS];
int tapCount = 0;
unsigned long firstTapTime = 0;
bool patternActive = false;
int lastButtonState = HIGH;
int buttonState = HIGH;
unsigned long lastDebounceTime = 0;
// ============================================
// Setup Function
// ============================================
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("\n╔════════════════════════════════════════╗");
Serial.println("║ Secret Knock - TinyML on ESP32 ║");
Serial.println("║ Pattern: PAUSE-QUICK-PAUSE ║");
Serial.println("╚════════════════════════════════════════╝\n");
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
// ============================================
// Initialize TinyML Model (Simulated)
// ============================================
Serial.println("=== Initializing TinyML Model ===");
delay(500);
Serial.println("✓ Model loaded from flash");
Serial.println("✓ Model architecture: 3-8-4-1 (Dense layers)");
Serial.print("✓ Model size: ");
Serial.print(MODEL_SIZE_BYTES);
Serial.println(" bytes");
Serial.println("✓ Inference engine: Optimized feedforward");
Serial.println("✓ Memory allocated: 10KB tensor arena");
Serial.println("✓ TinyML ready!\n");
Serial.println("✓ Door LOCKED");
Serial.println("Waiting for knock pattern...\n");
}
// ============================================
// Main Loop
// ============================================
void loop() {
int reading = digitalRead(BUTTON_PIN);
if (reading != lastButtonState) {
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > DEBOUNCE_DELAY) {
if (reading != buttonState) {
buttonState = reading;
if (buttonState == LOW) {
handleTap();
}
}
}
lastButtonState = reading;
if (patternActive && (millis() - firstTapTime > PATTERN_TIMEOUT)) {
Serial.println("⏱️ Timeout! Resetting...\n");
resetPattern();
}
}
// ============================================
// Tap Handler
// ============================================
void handleTap() {
unsigned long currentTime = millis();
if (tapCount == 0) {
tapTimes[0] = currentTime;
firstTapTime = currentTime;
patternActive = true;
tapCount = 1;
Serial.println("🔔 Tap 1 - Pattern started");
return;
}
if (tapCount < MAX_TAPS && patternActive) {
tapTimes[tapCount] = currentTime;
gaps[tapCount - 1] = tapTimes[tapCount] - tapTimes[tapCount - 1];
tapCount++;
Serial.print("🔔 Tap ");
Serial.print(tapCount);
Serial.print(" - Gap");
Serial.print(tapCount - 1);
Serial.print(": ");
Serial.print(gaps[tapCount - 2]);
Serial.println(" ms");
if (tapCount == MAX_TAPS) {
runTinyMLInference();
}
}
}
// ============================================
// TinyML Inference - Neural Network Simulation
// ============================================
void runTinyMLInference() {
Serial.println("\n─── TinyML Inference ───");
Serial.println("Feature Vector (timing gaps):");
Serial.print(" [");
Serial.print(gaps[0]);
Serial.print(", ");
Serial.print(gaps[1]);
Serial.print(", ");
Serial.print(gaps[2]);
Serial.println("]");
// Feature Normalization (0-1 scaling)
float norm1 = gaps[0] / 900.0;
float norm2 = gaps[1] / 900.0;
float norm3 = gaps[2] / 900.0;
Serial.print(" Normalized: [");
Serial.print(norm1, 3);
Serial.print(", ");
Serial.print(norm2, 3);
Serial.print(", ");
Serial.print(norm3, 3);
Serial.println("]");
// Neural Network Inference (Simplified)
Serial.println("\n🧠 Running Neural Network...");
// Layer 1: Weighted sum + bias
float hidden = weight1[0] * norm1 + weight1[1] * norm2 + weight1[2] * norm3 + bias1;
// Activation: ReLU
if (hidden < 0) hidden = 0;
// Output layer: Weighted sum + bias
float output_raw = weight2 * hidden + bias2;
// Activation: Sigmoid (0-1 probability)
float prediction = 1.0 / (1.0 + exp(-output_raw));
// Ensure prediction is in valid range
if (prediction < 0.0) prediction = 0.0;
if (prediction > 1.0) prediction = 1.0;
float confidence = prediction * 100.0;
Serial.print("✓ Hidden layer activation: ");
Serial.println(hidden, 4);
Serial.print("✓ Model Output (sigmoid): ");
Serial.println(prediction, 4);
Serial.print("✓ Confidence: ");
Serial.print(confidence, 1);
Serial.println("%");
// Decision
Serial.println("\n─── Decision ───");
if (prediction > MODEL_THRESHOLD) {
Serial.println("✓ PATTERN RECOGNIZED BY AI!");
Serial.print("✓ Neural Network Confidence: ");
Serial.print(confidence, 1);
Serial.println("%");
Serial.println("✓ Door UNLOCKED 🔓");
unlockDoor();
} else {
Serial.println("✗ PATTERN NOT RECOGNIZED");
Serial.print("✗ AI Rejection Confidence: ");
Serial.print((1 - prediction) * 100.0, 1);
Serial.println("%");
Serial.println("✗ Door LOCKED 🔒");
}
Serial.println("─────────────────────────────────────\n");
resetPattern();
}
// ============================================
// Door Control
// ============================================
void unlockDoor() {
digitalWrite(LED_PIN, HIGH);
delay(3000);
digitalWrite(LED_PIN, LOW);
Serial.println("🔒 Auto-locked\n");
}
void resetPattern() {
tapCount = 0;
patternActive = false;
firstTapTime = 0;
for (int i = 0; i < MAX_TAPS; i++) tapTimes[i] = 0;
for (int i = 0; i < MAX_GAPS; i++) gaps[i] = 0;
}