ESP8266 Door Access Control System
Loading...
Searching...
No Matches
esp3.ino
Go to the documentation of this file.
1/**
2 * @file esp3.ino
3 * @brief Door actuator, buzzer, and LED controller with MQTT integration.
4 *
5 * @defgroup esp3 ESP3 - Door Actuator
6 * @{
7 *
8 * @details
9 * This firmware runs on an ESP-based Arduino-compatible board and implements
10 * the final actuator stage of the access control system.
11 *
12 * Hardware components:
13 * - Servo motor for door locking mechanism
14 * - Status LEDs (red / green)
15 * - Piezo buzzer
16 *
17 * Functional responsibilities:
18 * - Locks and unlocks the door based on MQTT access decisions
19 * - Provides audible and visual feedback for access events
20 * - Automatically relocks the door after a configurable timeout
21 * - Supports an administrative servo override mode via MQTT
22 *
23 * This node does not make access decisions itself; it only reacts
24 * to authenticated results produced by the RFID and keypad nodes.
25 */
26
27
28#include <Servo.h>
29
30#include <ArduinoJson.h>
31#include <WiFiMqttClient.h>
32
33// ---------------- Network configuration ----------------
34
35/** @brief WiFi + MQTT client wrapper */
37
38/** @brief WiFi SSID */
39constexpr char WIFI_SSID[] = "Mathias2.4";
40/** @brief WiFi password */
41constexpr char WIFI_PASS[] = "mrbombasticcallmefantastic";
42
43/** @brief MQTT broker hostname */
44constexpr char MQTT_HOST[] = "maqiatto.com";
45/** @brief MQTT broker port */
46constexpr uint16_t MQTT_PORT = 1883;
47/** @brief MQTT username */
48constexpr char MQTT_USER[] = "hectorfoss@gmail.com";
49/** @brief MQTT password */
50constexpr char MQTT_PASS[] = "potter";
51
52/** @brief Unique device identifier used in MQTT topics */
53constexpr char DEVICE_ID[] = "door1";
54
55// -----------------------------------------------------------------------------
56// Pin configuration | esp3.fzz
57// -----------------------------------------------------------------------------
58
59/**
60 * @brief GPIO pin assignments.
61 *
62 * Refer to esp3.fzz for wiring details.
63 */
64constexpr uint8_t RED_PIN = 4; /**< Red LED (locked / denied) */
65constexpr uint8_t GREEN_PIN = 0; /**< Green LED (unlocked) */
66constexpr uint8_t BUZZER_PIN = 14; /**< Piezo buzzer */
67constexpr uint8_t SERVO_PIN = 13; /**< Servo signal pin */
68
69/** @brief Analog pin for potentiometer (admin servo control) */
70#define POT_PIN A0
71
72// -----------------------------------------------------------------------------
73// Timing configuration
74// -----------------------------------------------------------------------------
75
76/** @brief Door unlock duration (ms). */
77constexpr uint32_t UNLOCK_TIME_MS = 5000;
78
79/** @brief Timestamp (ms) until which the door remains unlocked. */
80uint32_t unlockUntil = 0;
81
82// -----------------------------------------------------------------------------
83// Global state
84// -----------------------------------------------------------------------------
85
86/** @brief Indicates whether the door is currently unlocked. */
87bool unlocked = false;
88
89/**
90 * @brief Access result enumeration.
91 */
92enum class AccessResult : uint8_t {
93 Denied, /**< Access denied */
94 Granted /**< Access granted */
95};
96
97/** @brief Result of keypad PIN verification. */
99
100/** @brief Indicates whether RFID access was granted. */
101bool rfidAccess = false;
102
103/** @brief Servo instance controlling the lock mechanism. */
105
106/** @brief Indicates whether the servo is currently in the open position. */
107bool servoOpen = false;
108
109/** @brief Current servo angle (derived from potentiometer in admin mode). */
111
112/** @brief Enables direct servo control via potentiometer (admin mode). */
113bool adminServoControl = false;
114
115// -----------------------------------------------------------------------------
116// Buzzer state machine
117// -----------------------------------------------------------------------------
118
119/*
120 * Timing arrays define alternating ON/OFF durations in milliseconds.
121 * Even indices (LEFT): buzzer ON durations
122 * Odd indices (RIGHT): buzzer OFF durations
123 */
124
125/** @brief Denied access: single long beep. Extra long to scare */
126const unsigned int deniedTimings[] = {
127 2000
128};
129
130/** @brief Lock sound: long-long-long pattern. */
131const unsigned int lockTimings[] = {
132 250, 120,
133 250, 120,
134 250
135};
136
137/** @brief Unlock sound: short-short pattern. */
138const unsigned int unlockTimings[] = {
139 125, 120,
140 125
141};
142
143/** @brief Keypad tap sound: short beep. */
144const unsigned int beepTimings[] = {
145 125
146};
147
148/**
149 * @brief Buzzer state enumeration.
150 */
152 BUZZER_IDLE, /**< No sound playing */
153 BUZZER_ON, /**< Buzzer currently active */
154 BUZZER_OFF /**< Silent gap between beeps */
155};
156
157/**
158 * @brief Describes a buzzer sound pattern.
159 */
161 const unsigned int* timings; /**< Pointer to timing array */
162 uint8_t length; /**< Number of timing entries */
163};
164
165/** @brief Current buzzer state. */
167
168/** @brief Currently active buzzer pattern. */
170
171/** @brief Index into the timing array. */
172uint8_t stepIndex = 0;
173
174/** @brief Timestamp of last buzzer state change. */
175unsigned long lastChange = 0;
176
177/**
178 * @brief Starts playing a buzzer pattern.
179 *
180 * @param timings Pointer to timing array.
181 * @param length Number of elements in the timing array.
182 */
183void playPattern(const unsigned int* timings, uint8_t length) {
184
185 // Prevent overlapping sound patterns
186 if (buzzerState != BUZZER_IDLE) return;
187
188 currentPattern.timings = timings;
189 currentPattern.length = length;
190
191 stepIndex = 0;
193 lastChange = millis();
194
195 digitalWrite(BUZZER_PIN, HIGH); // Start with buzzer ON
196}
197
198/**
199 * @brief Advances the buzzer state machine.
200 *
201 * Must be called frequently from the main loop.
202 */
204 if (buzzerState == BUZZER_IDLE) return;
205
206 unsigned long now = millis();
207
208 // Wait until current timing interval expires
209 if (now - lastChange < currentPattern.timings[stepIndex]) return;
210
211 lastChange = now;
212 stepIndex++;
213
214 // End of pattern reached
215 if (stepIndex >= currentPattern.length) {
216 digitalWrite(BUZZER_PIN, LOW);
218 return;
219 }
220
221 // Toggle buzzer state
222 if (buzzerState == BUZZER_ON) {
223 digitalWrite(BUZZER_PIN, LOW);
225 } else {
226 digitalWrite(BUZZER_PIN, HIGH);
228 }
229}
230
231/**
232 * @brief Immediately stops any active buzzer sound.
233 */
236 digitalWrite(BUZZER_PIN, LOW);
237}
238
239/** @brief Plays lock sound pattern. */
241 stopBuzzer();
243 sizeof(lockTimings) / sizeof(lockTimings[0]));
244}
245
246/** @brief Plays unlock sound pattern. */
248 stopBuzzer();
250 sizeof(unlockTimings) / sizeof(unlockTimings[0]));
251}
252
253/** @brief Plays denied-access sound pattern. */
255 stopBuzzer();
257 sizeof(deniedTimings) / sizeof(deniedTimings[0]));
258}
259
260/** @brief Plays keypad tap sound. */
262 stopBuzzer();
264 sizeof(beepTimings) / sizeof(beepTimings[0]));
265}
266
267// -----------------------------------------------------------------------------
268// Helper functions
269// -----------------------------------------------------------------------------
270
271/**
272 * @brief Forces the system into a locked idle state.
273 *
274 * - Resets access state
275 * - Sets LEDs to "locked"
276 * - Moves servo to closed position if needed
277 */
278static void forceLock() {
280 unlocked = false;
281
282 digitalWrite(RED_PIN, HIGH);
283 digitalWrite(GREEN_PIN, LOW);
284
285 if (servoOpen) {
286 lock_servo.write(0); // Move servo to locked position
287 servoOpen = false;
288 }
289}
290
291// -----------------------------------------------------------------------------
292// MQTT callback
293// -----------------------------------------------------------------------------
294
295/**
296 * @brief MQTT message callback handler.
297 *
298 * Handles:
299 * - Keypad access responses (unlock / deny)
300 * - RFID access denials
301 * - Keypad tap beep feedback
302 * - Admin servo control enable/disable
303 *
304 * @param topic MQTT topic string.
305 * @param payload Raw payload bytes.
306 * @param length Payload length.
307 */
308void callback(char* topic, byte* payload, unsigned int length) {
309
310 // Ignore empty MQTT messages
311 if (length == 0) return;
312
313 StaticJsonDocument<512> doc;
314 DeserializationError err = deserializeJson(doc, payload, length);
315
316 // Abort if JSON parsing fails
317 if (err) {
318 Serial.print("JSON parse failed: ");
319 Serial.println(err.c_str());
320 return;
321 }
322
323 // ---------------------------------------------------------------------------
324 // Keypad PIN verification response
325 // ---------------------------------------------------------------------------
326 if (strcmp(topic, net.makeTopic("access/keypad_response").c_str()) == 0) {
327
328 // Ignore keypad responses during admin servo control
329 if (adminServoControl) return;
330
331 accessGranted = (doc["response"]["accessGranted"] | false)
334
336 Serial.println("Access Denied");
338 forceLock();
339 return;
340 }
341
342 // Access granted: unlock door
343 Serial.println("Unlocking door");
345
346 lock_servo.write(180); // Move servo to unlocked position
347 servoOpen = true;
348
349 digitalWrite(GREEN_PIN, HIGH);
350 digitalWrite(RED_PIN, LOW);
351
352 unlocked = true;
353 unlockUntil = millis() + UNLOCK_TIME_MS;
354 }
355
356 // ---------------------------------------------------------------------------
357 // RFID access response (only react to denial)
358 // ---------------------------------------------------------------------------
359 else if (strcmp(topic, net.makeTopic("access/response").c_str()) == 0) {
360
361 rfidAccess = (doc["response"]["hasAccess"] | false)
362 ? true
363 : false;
364
365 if (!rfidAccess) {
366 Serial.println("Access Denied");
368 forceLock();
369 return;
370 }
371 }
372
373 // ---------------------------------------------------------------------------
374 // Keypad tap feedback
375 // ---------------------------------------------------------------------------
376 else if (strcmp(topic, net.makeTopic("keypad/beep").c_str()) == 0) {
377
378 // Only beep if RFID access is valid
379 if (!rfidAccess) return;
380
381 playTapSound();
382 }
383
384 // ---------------------------------------------------------------------------
385 // Admin servo control enable/disable
386 // ---------------------------------------------------------------------------
387 else if (strcmp(topic, net.makeTopic("admin/servo_control").c_str()) == 0) {
388
389 adminServoControl = (doc["data"]["adminServoControl"] | false)
390 ? true
391 : false;
392
393 if (!adminServoControl) {
394 Serial.println("Admin servo control disabled");
395 forceLock(); // Reset servo to locked state
396 return;
397 }
398
399 Serial.println("Admin servo control enabled");
400 }
401}
402
403// -----------------------------------------------------------------------------
404// Setup
405// -----------------------------------------------------------------------------
406
407/**
408 * @brief Arduino setup function.
409 *
410 * Initializes GPIOs, servo, WiFi,
411 * MQTT client, and topic subscriptions.
412 */
413void setup() {
414
415 pinMode(RED_PIN, OUTPUT);
416 pinMode(GREEN_PIN, OUTPUT);
417 pinMode(BUZZER_PIN, OUTPUT);
418
419 lock_servo.attach(SERVO_PIN);
420
421 // Initialize outputs to safe state
422 digitalWrite(RED_PIN, LOW);
423 digitalWrite(GREEN_PIN, LOW);
424 digitalWrite(BUZZER_PIN, LOW);
425 lock_servo.write(0); // Locked position
426
427 servoAngle = 0;
428
429 delay(100);
430 Serial.begin(115200);
431
432 net.begin(
433 WIFI_SSID,
434 WIFI_PASS,
435 MQTT_HOST,
436 MQTT_PORT,
437 MQTT_USER,
438 MQTT_PASS,
439 DEVICE_ID,
440 "site1"
441 );
442
443 Serial.println("WiFi & MQTT ready");
444
445 net.setCallback(callback);
446
447 // Subscribe to required MQTT topics
448 Serial.printf("access/response MQTT subscribe %s\n",
449 net.subscribe(net.makeTopic("access/response").c_str()) ? "OK" : "FAILED");
450
451 Serial.printf("access/keypad_response MQTT subscribe %s\n",
452 net.subscribe(net.makeTopic("access/keypad_response").c_str()) ? "OK" : "FAILED");
453
454 Serial.printf("keypad/beep MQTT subscribe %s\n",
455 net.subscribe(net.makeTopic("keypad/beep").c_str()) ? "OK" : "FAILED");
456
457 Serial.printf("admin/servo_control MQTT subscribe %s\n",
458 net.subscribe(net.makeTopic("admin/servo_control").c_str()) ? "OK" : "FAILED");
459}
460
461// -----------------------------------------------------------------------------
462// Main loop
463// -----------------------------------------------------------------------------
464
465/**
466 * @brief Arduino main loop.
467 *
468 * Handles:
469 * - MQTT processing
470 * - Buzzer state machine updates
471 * - Servo admin override mode
472 * - Automatic relocking after timeout
473 */
474void loop() {
475 net.loop();
476 yield();
477
478 // Advance buzzer state machine
479 updateBuzzer();
480
481 // ---------------------------------------------------------------------------
482 // Admin servo control mode
483 // ---------------------------------------------------------------------------
484 if (adminServoControl) {
485
486 // Map potentiometer value to servo angle
487 servoAngle = (int)(analogRead(POT_PIN) / 1023.0f * 180.0f);
488 servoAngle = constrain(servoAngle, 0, 180);
489
490 lock_servo.write(servoAngle);
491 return;
492 }
493
494 // ---------------------------------------------------------------------------
495 // Automatic relock after unlock timeout
496 // ---------------------------------------------------------------------------
497 const uint32_t now = millis();
498
499 if (unlocked && (int32_t)(now - unlockUntil) >= 0) {
500 Serial.println("Locking door");
502 forceLock();
503 }
504}
Lightweight WiFi + MQTT helper wrapper for ESP-based Arduino systems.
Combined WiFi and MQTT client abstraction.
constexpr char WIFI_PASS[]
WiFi password.
constexpr char DEVICE_ID[]
Unique device identifier used in MQTT topics.
WifiMqttClient net
WiFi + MQTT client wrapper.
constexpr char MQTT_USER[]
MQTT username.
constexpr char MQTT_PASS[]
MQTT password.
AccessResult accessGranted
Result of PIN verification.
constexpr char MQTT_HOST[]
MQTT broker hostname.
AccessResult
Access result enumeration.
constexpr uint32_t UNLOCK_TIME_MS
Door unlock display duration (ms).
constexpr uint16_t MQTT_PORT
MQTT broker port.
void callback(char *topic, byte *payload, unsigned int length)
MQTT message callback handler.
constexpr char WIFI_SSID[]
WiFi SSID.
AccessResult rfidAccess
Result of RFID authentication.
void playDeniedSound()
Plays denied-access sound pattern.
Definition esp3.ino:254
void playTapSound()
Plays keypad tap sound.
Definition esp3.ino:261
constexpr uint8_t RED_PIN
GPIO pin assignments.
Definition esp3.ino:64
const unsigned int deniedTimings[]
Denied access: single long beep. Extra long to scare.
Definition esp3.ino:126
void updateBuzzer()
Advances the buzzer state machine.
Definition esp3.ino:203
BuzzerPattern currentPattern
Currently active buzzer pattern.
Definition esp3.ino:169
void playLockSound()
Plays lock sound pattern.
Definition esp3.ino:240
void setup()
Arduino setup function.
Definition esp3.ino:413
bool unlocked
Indicates whether the door is currently unlocked.
Definition esp3.ino:87
uint32_t unlockUntil
Timestamp (ms) until which the door remains unlocked.
Definition esp3.ino:80
Servo lock_servo
Servo instance controlling the lock mechanism.
Definition esp3.ino:104
const unsigned int lockTimings[]
Lock sound: long-long-long pattern.
Definition esp3.ino:131
BuzzerState buzzerState
Current buzzer state.
Definition esp3.ino:166
void playPattern(const unsigned int *timings, uint8_t length)
Starts playing a buzzer pattern.
Definition esp3.ino:183
uint8_t servoAngle
Current servo angle (derived from potentiometer in admin mode).
Definition esp3.ino:110
void stopBuzzer()
Immediately stops any active buzzer sound.
Definition esp3.ino:234
void playUnlockSound()
Plays unlock sound pattern.
Definition esp3.ino:247
constexpr uint8_t SERVO_PIN
Definition esp3.ino:67
const unsigned int * timings
Definition esp3.ino:161
uint8_t length
Definition esp3.ino:162
#define POT_PIN
Analog pin for potentiometer (admin servo control).
Definition esp3.ino:70
void callback(char *topic, byte *payload, unsigned int length)
MQTT message callback handler.
Definition esp3.ino:308
bool servoOpen
Indicates whether the servo is currently in the open position.
Definition esp3.ino:107
bool adminServoControl
Enables direct servo control via potentiometer (admin mode).
Definition esp3.ino:113
const unsigned int beepTimings[]
Keypad tap sound: short beep.
Definition esp3.ino:144
uint8_t stepIndex
Index into the timing array.
Definition esp3.ino:172
constexpr uint8_t GREEN_PIN
Definition esp3.ino:65
BuzzerState
Buzzer state enumeration.
Definition esp3.ino:151
unsigned long lastChange
Timestamp of last buzzer state change.
Definition esp3.ino:175
const unsigned int unlockTimings[]
Unlock sound: short-short pattern.
Definition esp3.ino:138
constexpr uint8_t BUZZER_PIN
Definition esp3.ino:66
void loop()
Arduino main loop.
Definition esp3.ino:474
static void forceLock()
Forces the system into a locked idle state.
Definition esp3.ino:278
@ BUZZER_ON
Definition esp3.ino:153
@ BUZZER_OFF
Definition esp3.ino:154
@ BUZZER_IDLE
Definition esp3.ino:152
Describes a buzzer sound pattern.
Definition esp3.ino:160