#include <Keypad.h>
#include <ESP32Servo.h>
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <WiFi.h>
#include <PubSubClient.h>
// ----- CONFIGURATION -----
const char* ssid = "Wokwi-GUEST";
const char* password = "";
const char* mqtt_server = "broker.hivemq.com";
const int mqtt_port = 1883;
const char* mqtt_topic = "home_acces/door";
// ----- OBJETS -----
WiFiClient espClient;
PubSubClient client(espClient);
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1 // Reset pin non utilisé sur ESP32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// --- Config Keypad ---
const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
byte rowPins[ROWS] = {4, 0, 2, 15};
byte colPins[COLS] = {18, 5, 17, 16};
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
// --- Config Servo ---
Servo servo;
int servoPin = 13;
// --- Config LED ---
int ledGreen = 12; // LED accès autorisé
int ledRed = 14; // LED accès refusé
// --- PIN correct ---
String pinCode = "1234";
String inputCode = "";
// --- Sécurité ---
int essaisRestants = 3;
int countdown = 10; // durée du blocage en secondes
unsigned long lockoutStart = 0;
bool isLockedOut = false;
// ----- CALLBACK MQTT -----
void callback(char* topic, byte* payload, unsigned int length) {
String message;
for (unsigned int i = 0; i < length; i++) {
message += (char)payload[i];
}
Serial.print("Message reçu sur le topic ");
Serial.print(topic);
Serial.print(" : ");
Serial.println(message);
// Actions test
if (message == "open") {
Serial.println("Porte ouvert");
display.clearDisplay();
display.setCursor(0, 0);
display.println("Porte ouvert");
display.display();
digitalWrite(ledGreen, HIGH);
digitalWrite(ledRed, LOW);
servo.write(90); // ouvre la porte
delay(5000); // maintien 5s
servo.write(0); // referme
digitalWrite(ledGreen, LOW);
} else if (message == "close") {
Serial.println("Commande: FERMER la porte");
display.clearDisplay();
display.setCursor(0, 0);
display.println("Porte ferme");
display.display();
// servo.write(0);
}
}
// ----- RECONNEXION MQTT -----
void reconnect() {
while (!client.connected()) {
Serial.print("Connexion au broker MQTT...");
String clientId = "ESP32Client-" + String(random(0xffff), HEX);
if (client.connect(clientId.c_str())) {
Serial.println("Connecté !");
client.subscribe(mqtt_topic);
Serial.print("Abonné au topic: ");
Serial.println(mqtt_topic);
} else {
Serial.print("Échec, rc=");
Serial.print(client.state());
Serial.println(" Nouvelle tentative dans 5s");
delay(5000);
}
}
}
void setup() {
Serial.begin(115200);
pinMode(ledGreen, OUTPUT);
pinMode(ledRed, OUTPUT);
servo.attach(servoPin);
servo.write(0); // porte fermée
// Initialisation I2C OLED (SDA=21, SCL=22)
Wire.begin(21, 22);
// Connexion WiFi
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi perdu, reconnexion...");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi reconnecté !");
}
// Configuration MQTT
client.setServer(mqtt_server, mqtt_port);
client.setCallback(callback);
servo.attach(servoPin);
servo.write(0); // porte fermée
// Initialisation I2C OLED (SDA=21, SCL=22)
Wire.begin(22, 23);
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;);
}
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.println("Entrer Code");
display.display();
}
// ----- Fonction envoie status -----
void sendStatus(String status) {
if (!client.connected()) reconnect();
client.publish(mqtt_topic, status.c_str());
}
void loop() {
// Vérifie la connexion WiFi
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi perdu, reconnexion...");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi reconnecté !");
}
// Vérifie la connexion MQTT
if (!client.connected()) {
reconnect();
}
client.loop();
// Gestion du blocage
if (isLockedOut) {
unsigned long elapsed = (millis() - lockoutStart) / 1000;
int remaining = countdown - elapsed;
if (remaining > 0) {
display.clearDisplay();
display.setCursor(0, 0);
display.setTextSize(2);
display.print("Bloque: ");
display.println(remaining);
display.display();
delay(200); // petit délai pour éviter le flicker
return; // bloque le clavier tant que c’est verrouillé
} else {
isLockedOut = false;
essaisRestants = 3;
display.clearDisplay();
display.setCursor(0, 0);
display.println("Reessayez");
display.display();
}
}
char key = keypad.getKey();
if (key) {
if (key == '#') { // bouton "Entrée"
if (inputCode == pinCode) {
Serial.println("Acces autorise !");
digitalWrite(ledGreen, HIGH);
digitalWrite(ledRed, LOW);
display.clearDisplay();
display.setCursor(0, 0);
display.println("Correct!");
display.setCursor(0, 20);
display.println("OUVERT");
sendStatus("Porte ouvert");
display.display();
servo.write(90); // ouvre la porte
delay(5000); // maintien 5s
servo.write(0); // referme
digitalWrite(ledGreen, LOW);
essaisRestants = 3; // reset des essais
} else {
essaisRestants--;
Serial.println("Code incorrect !");
digitalWrite(ledRed, HIGH);
delay(500);
digitalWrite(ledRed, LOW);
display.clearDisplay();
display.setCursor(0, 0);
if (essaisRestants > 0) {
display.printf("Incorrect!\nReste %d essais", essaisRestants);
} else {
display.println("Bloque!");
isLockedOut = true;
lockoutStart = millis();// démarre le chrono
sendStatus("3 tentatives");
digitalWrite(ledRed, HIGH);
delay(500);
digitalWrite(ledRed, LOW);
}
display.display();
}
inputCode = ""; // reset entrée après validation
} else if (key == '*') { // bouton "Effacer" (backspace)
if (inputCode.length() > 0) {
inputCode.remove(inputCode.length() - 1); // supprime dernier caractère
}
display.clearDisplay();
display.setCursor(0, 0);
display.println("Saisie:");
display.setCursor(0, 20);
display.println(inputCode);
display.display();
} else {
inputCode += key; // ajoute chiffre
display.clearDisplay();
display.setCursor(0, 0);
display.println("Saisie:");
display.setCursor(0, 20);
display.println(inputCode);
display.display();
}
}
}