ESP8266 Door Access Control System
Loading...
Searching...
No Matches
esp2_keypad.ino
Go to the documentation of this file.
1/**
2 * @file esp2_keypad.ino
3 * @brief Keypad-based PIN entry controller with MQTT backend integration.
4 *
5 * @defgroup esp2 ESP2 - Keypad
6 * @{
7 *
8 * @details
9 * This firmware runs on an ESP-based Arduino-compatible board and implements
10 * the second stage of the access control system.
11 *
12 * Hardware components:
13 * - 4x4 matrix keypad using the Keypad library
14 *
15 * Functional responsibilities:
16 * - Accepts PIN input from the user
17 * - Provides keypress feedback (beeps)
18 * - Publishes PIN entry progress and submissions via MQTT
19 *
20 * The keypad is only active after successful RFID authentication,
21 * which is signaled asynchronously via MQTT.
22 */
23
24
25#include <Arduino.h>
26#include <Keypad.h>
27// #include <string.h>
28#include <ArduinoJson.h>
29
30#include <WiFiMqttClient.h>
31
32// ---------------- Network configuration ----------------
33
34/** @brief WiFi + MQTT client wrapper */
36
37/** @brief WiFi SSID */
38constexpr char WIFI_SSID[] = "Mathias2.4";
39/** @brief WiFi password */
40constexpr char WIFI_PASS[] = "mrbombasticcallmefantastic";
41
42/** @brief MQTT broker hostname */
43constexpr char MQTT_HOST[] = "maqiatto.com";
44/** @brief MQTT broker port */
45constexpr uint16_t MQTT_PORT = 1883;
46/** @brief MQTT username */
47constexpr char MQTT_USER[] = "hectorfoss@gmail.com";
48/** @brief MQTT password */
49constexpr char MQTT_PASS[] = "potter";
50
51/** @brief Unique device identifier used in MQTT topics */
52constexpr char DEVICE_ID[] = "door1";
53
54// ---------------- Keypad configuration ----------------
55
56/** @brief Number of rows in the keypad matrix */
57const byte ROWS = 4;
58
59/** @brief Number of columns in the keypad matrix */
60const byte COLS = 4;
61
62/**
63 * @brief Logical keypad layout.
64 *
65 * Defines the character returned for each row/column intersection.
66 */
67char keys[ROWS][COLS] = {
68 {'1','2','3','A'},
69 {'4','5','6','B'},
70 {'7','8','9','C'},
71 {'*','0','#','D'}
72};
73
74
75/**
76 * @brief GPIO pins connected to keypad rows.
77 *
78 * Order must match ROWS definition.
79 */
80byte rowPins[ROWS] = {16, 5, 4, 0}; /**< D0, D1, D2, D3 */
81
82/**
83 * @brief GPIO pins connected to keypad columns.
84 *
85 * Order must match COLS definition.
86 */
87byte colPins[COLS] = {2, 14, 12, 13}; /**< D4, D5, D6, D7 */
88
89/*
90 * Physical wiring reference:
91 * R1 -> D8, R2 -> D7, R3 -> D6, R4 -> D5
92 * C1 -> D4, C2 -> D3, C3 -> D2, C4 -> D1
93 *
94 * ESP8266 mapping:
95 * D1=5, D2=4, D3=0, D4=2,
96 * D5=14, D6=12, D7=13, D8=15
97 */
98
99/**
100 * @brief Keypad instance handling scanning and debouncing.
101 */
102Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
103
104// ---------------- Code handling ----------------
105
106/** @brief Required PIN length */
107constexpr uint8_t CODE_LENGTH = 4;
108
109/**
110 * @brief Input buffer for PIN digits.
111 *
112 * One extra byte is reserved for the null terminator.
113 */
114char input[CODE_LENGTH + 1] = {0};
115
116/** @brief Current index into the PIN buffer */
117uint8_t currentIdx = 0;
118
119/**
120 * @brief Indicates whether keypad input is currently enabled.
121 *
122 * This flag is controlled via MQTT messages from the access controller.
123 */
124bool kpEnabled = false;
125
126// -----------------------------------------------------------------------------
127// MQTT callback
128// -----------------------------------------------------------------------------
129
130/**
131 * @brief MQTT message callback handler.
132 *
133 * Handles:
134 * - Enabling keypad input after RFID access is granted
135 * - Disabling keypad input after PIN validation completes
136 *
137 * @param topic MQTT topic string.
138 * @param payload Raw payload bytes.
139 * @param length Payload length.
140 */
141void callback(char* topic, byte* payload, unsigned int length) {
142
143 // Ignore empty MQTT messages
144 if (length == 0) return;
145
146 StaticJsonDocument<512> doc;
147
148 // Parse JSON payload
149 DeserializationError err = deserializeJson(doc, payload, length);
150
151 // Abort on JSON parsing failure
152 if (err) {
153 Serial.print("JSON parse failed: ");
154 Serial.println(err.c_str());
155 return;
156 }
157
158 // ---------------------------------------------------------------------------
159 // RFID access response: enable or disable keypad
160 // ---------------------------------------------------------------------------
161 if (strcmp(topic, net.makeTopic("access/response").c_str()) == 0) {
162
163 // Enable keypad only if RFID access was granted
164 kpEnabled = (doc["response"]["hasAccess"] | false)
165 ? true
166 : false;
167 }
168 // ---------------------------------------------------------------------------
169 // PIN verification response: always disable keypad afterward
170 // ---------------------------------------------------------------------------
171 else if (strcmp(topic, net.makeTopic("access/keypad_response").c_str()) == 0) {
172
173 // Prevent further input until next RFID authorization
174 kpEnabled = false;
175 }
176}
177
178// -----------------------------------------------------------------------------
179// Setup
180// -----------------------------------------------------------------------------
181
182/**
183 * @brief Arduino setup function.
184 *
185 * Initializes Serial output, WiFi connection,
186 * MQTT client, and topic subscriptions.
187 */
188void setup() {
189 Serial.begin(115200);
190
191 net.begin(
192 WIFI_SSID,
193 WIFI_PASS,
194 MQTT_HOST,
195 MQTT_PORT,
196 MQTT_USER,
197 MQTT_PASS,
198 DEVICE_ID,
199 "site1"
200 );
201
202 Serial.println("Keypad + MQTT ready");
203
204 net.setCallback(callback);
205
206 // Subscribe to access control topics
207 Serial.printf("access/response MQTT subscribe %s\n",
208 net.subscribe(net.makeTopic("access/response").c_str()) ? "OK" : "FAILED");
209
210 Serial.printf("access/keypad_response MQTT subscribe %s\n",
211 net.subscribe(net.makeTopic("access/keypad_response").c_str()) ? "OK" : "FAILED");
212}
213
214// -----------------------------------------------------------------------------
215// Helper functions
216// -----------------------------------------------------------------------------
217
218/**
219 * @brief Resets the PIN input buffer and index.
220 *
221 * Clears any partially entered PIN and prepares
222 * the buffer for new input.
223 */
225 memset(input, 0, sizeof(input)); // Clear buffer contents
226 currentIdx = 0; // Reset write index
227}
228
229/**
230 * @brief Publishes keypad tap feedback via MQTT.
231 *
232 * Sends the current PIN length after each valid keypress,
233 * allowing external systems to provide visual or audio feedback.
234 */
236 StaticJsonDocument<64> data;
237 data["event"] = "KP_tap";
238 data["pinlength"] = currentIdx;
239
240 net.publishJson("keypad/tap", data);
241}
242
243// -----------------------------------------------------------------------------
244// Main loop
245// -----------------------------------------------------------------------------
246
247/**
248 * @brief Arduino main loop.
249 *
250 * Handles:
251 * - MQTT client processing
252 * - Keypad scanning
253 * - PIN entry logic
254 * - PIN submission and reset
255 */
256void loop() {
257 net.loop(); // Process MQTT traffic
258 yield(); // Allow background WiFi tasks
259
260 // Read keypad state (non-blocking)
261 char key = keypad.getKey();
262
263 // Ignore input if no key pressed or keypad is disabled
264 if (key == NO_KEY || !kpEnabled) {
265 return;
266 }
267
268 // ---------------------------------------------------------------------------
269 // Numeric key input
270 // ---------------------------------------------------------------------------
271 if (key >= '0' && key <= '9') {
272
273 // Only accept input if buffer is not full
274 if (currentIdx < CODE_LENGTH) {
275 input[currentIdx++] = key; // Store digit
276 input[currentIdx] = '\0'; // Maintain null-terminated string
277
278 Serial.print("Key: ");
279 Serial.println(key);
280 }
281 }
282
283 // ---------------------------------------------------------------------------
284 // Submit PIN using '#'
285 // ---------------------------------------------------------------------------
286 else if (key == '#') {
287
288 // Only submit if required PIN length is reached
289 if (currentIdx == CODE_LENGTH) {
290 Serial.print("Submitting code: ");
291 Serial.println(input);
292
293 // Build JSON payload for PIN submission
294 StaticJsonDocument<64> data;
295 data["event"] = "KP_try";
296 data["code"] = input;
297
298 net.publishJson("keypad/submit", data);
299 }
300 else {
301 Serial.println("Code too short, resetting.");
302 }
303
304 // Always reset buffer after submission attempt
305 reset_code();
306 }
307
308 // ---------------------------------------------------------------------------
309 // Clear input using '*'
310 // ---------------------------------------------------------------------------
311 else if (key == '*') {
312 reset_code();
313 Serial.println("Input cleared");
314 }
315
316 // Ignore unsupported keys (A-D)
317 else {
318 return;
319 }
320
321 // Any valid keypress is treated as a tap event
322 publishTap();
323}
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.
constexpr char MQTT_HOST[]
MQTT broker hostname.
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.
Keypad keypad
Keypad instance handling scanning and debouncing.
bool kpEnabled
Indicates whether keypad input is currently enabled.
void setup()
Arduino setup function.
byte rowPins[ROWS]
GPIO pins connected to keypad rows.
constexpr uint8_t CODE_LENGTH
Required PIN length.
const byte ROWS
Number of rows in the keypad matrix.
char keys[ROWS][COLS]
Logical keypad layout.
void reset_code()
Resets the PIN input buffer and index.
byte colPins[COLS]
GPIO pins connected to keypad columns.
char input[CODE_LENGTH+1]
Input buffer for PIN digits.
void callback(char *topic, byte *payload, unsigned int length)
MQTT message callback handler.
void publishTap()
Publishes keypad tap feedback via MQTT.
const byte COLS
Number of columns in the keypad matrix.
void loop()
Arduino main loop.
uint8_t currentIdx
Current index into the PIN buffer.