├── LICENSE ├── README.md └── esp8266-gate-control.ino /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Ofer Buskila 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP8266 Gate Control System 2 | 3 | An ESP8266-based gate control system that automates gate operations based on real-time data fetched from a server and allows manual control via a button. The system provides visual feedback through LEDs and logs detailed information via serial communication for debugging and monitoring purposes. 4 | 5 | ## Table of Contents 6 | 7 | - [Features](#features) 8 | - [Hardware Requirements](#hardware-requirements) 9 | - [Software Requirements](#software-requirements) 10 | - [Setup and Installation](#setup-and-installation) 11 | - [Configuration](#configuration) 12 | - [Operation](#operation) 13 | - [Functionality Overview](#functionality-overview) 14 | - [Contributing](#contributing) 15 | - [License](#license) 16 | 17 | ## Features 18 | 19 | - **Automated Gate Control:** Opens the gate when a specific term is found in data fetched from a server. 20 | - **Manual Gate Control:** Allows manual gate opening via a physical button. 21 | - **Visual Feedback:** Uses LEDs to indicate system status, errors, and operations. 22 | - **Logging:** Outputs detailed logs with timestamps over serial communication for debugging. 23 | - **Secure Data Fetching:** Uses HTTPS to securely fetch data from the server. 24 | 25 | ## Hardware Requirements 26 | 27 | - **ESP8266 Microcontroller** (e.g., NodeMCU ESP8266) 28 | - **LEDs:** 29 | - Green LED (GPIO12 / D6) 30 | - Blue LED (GPIO13 / D7) 31 | - Red LED (GPIO15 / D8) 32 | - Built-in LED (GPIO2 / D4) 33 | - **Gate Control Pins:** 34 | - Gate Pin 1 (GPIO14 / D5) 35 | - Gate Pin 2 (GPIO16 / D0) 36 | - **Button:** Connected to GPIO4 (D2) with an internal pull-up resistor 37 | - **WiFi Network:** Access to a 2.4GHz WiFi network 38 | 39 | ## Software Requirements 40 | 41 | - **Arduino IDE** (version 1.8 or higher) 42 | - **ESP8266 Board Package** (installed via Arduino Boards Manager) 43 | - **Arduino Libraries:** 44 | - `ESP8266WiFi.h` 45 | - `WiFiClientSecure.h` 46 | - `ArduinoJson.h` (version compatible with the code) 47 | 48 | ## Setup and Installation 49 | 50 | 1. **Clone or Download the Repository:** 51 | 52 | ```bash 53 | git clone https://github.com/yourusername/esp8266-gate-control.git 54 | -------------------------------------------------------------------------------- /esp8266-gate-control.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // ======================= Configuration ======================= 6 | 7 | // WiFi Credentials 8 | const char* ssid = "********"; 9 | const char* password = "*******"; 10 | 11 | // Server Details 12 | const char* host = "www.oref.org.il"; 13 | const int httpsPort = 443; 14 | const char* url = "/warningMessages/alert/Alerts.json"; 15 | 16 | // LED Pin Definitions 17 | const uint8_t GREEN_LED_PIN = D6; // GPIO12 18 | const uint8_t BLUE_LED_PIN = D7; // GPIO13 19 | const uint8_t RED_LED_PIN = D8; // GPIO15 20 | const uint8_t BUILTIN_LED_PIN = D4; // GPIO2 21 | 22 | // Gate Control Pins 23 | const uint8_t GATE_PIN_1 = D5; // GPIO14 24 | const uint8_t GATE_PIN_2 = D0; // GPIO16 25 | 26 | // Button Pin 27 | const uint8_t BUTTON_PIN = D2; // GPIO4 28 | 29 | // Search Term (in Hebrew) 30 | const char* searchTerm = "קריית"; 31 | 32 | // Button Debounce Variables 33 | bool lastButtonState = HIGH; 34 | unsigned long lastDebounceTime = 0; 35 | const unsigned long debounceDelay = 50; // in milliseconds 36 | 37 | // Fetch Data Variables 38 | unsigned long lastFetchTime = 0; 39 | const unsigned long fetchInterval = 2000; // 2 seconds 40 | 41 | // ======================= Function Prototypes ======================= 42 | void logSerial(const String& message); 43 | void turnOffLEDs(); 44 | void blinkLED(uint8_t pin, unsigned long delayTime, int times); 45 | void blinkLEDs(uint8_t pin1, uint8_t pin2, unsigned long delayTime, int times); 46 | void setSolidLED(uint8_t pin, bool state); 47 | void openGate(int cycles); 48 | void connectToWiFi(); 49 | void handleButtonPress(); 50 | void fetchDataAndProcess(); 51 | 52 | // Sub-methods for openGate 53 | void openTerminal(); 54 | void clickOpenGateRapidly(); 55 | void waitForDelay(unsigned long duration); 56 | void reActivateGatePin2(); 57 | void closeTerminal(); 58 | 59 | // Optional: Function to set RGB LED color (if applicable) 60 | void setRGBColor(int red, int green, int blue); 61 | 62 | // ======================= Setup Function ======================= 63 | void setup() { 64 | // Initialize Serial for debugging 65 | Serial.begin(115200); 66 | logSerial("\nStarting NODEMCU ESP8266..."); 67 | 68 | // Initialize LED Pins as OUTPUT 69 | pinMode(RED_LED_PIN, OUTPUT); 70 | pinMode(GREEN_LED_PIN, OUTPUT); 71 | pinMode(BLUE_LED_PIN, OUTPUT); 72 | pinMode(BUILTIN_LED_PIN, OUTPUT); 73 | 74 | // Initialize Gate Control Pins as OUTPUT 75 | pinMode(GATE_PIN_1, OUTPUT); 76 | pinMode(GATE_PIN_2, OUTPUT); 77 | 78 | // Initialize Button Pin as INPUT_PULLUP 79 | pinMode(BUTTON_PIN, INPUT_PULLUP); 80 | 81 | // Turn off all LEDs initially 82 | turnOffLEDs(); 83 | digitalWrite(BUILTIN_LED_PIN, LOW); // Turn off the built-in LED 84 | logSerial("All LEDs turned off. Built-in LED is OFF."); 85 | 86 | // Connect to WiFi 87 | connectToWiFi(); 88 | } 89 | 90 | // ======================= Main Loop Function ======================= 91 | void loop() { 92 | // Handle Button Press with Debouncing 93 | handleButtonPress(); 94 | 95 | // Check WiFi Connection 96 | if (WiFi.status() != WL_CONNECTED) { 97 | logSerial("WiFi not connected! Attempting to reconnect..."); 98 | blinkLED(BLUE_LED_PIN, 500, 3); // Blink blue LED 3 times 99 | connectToWiFi(); 100 | // Continue the loop to allow handling button presses 101 | } 102 | 103 | // Fetch Data from Server and Process periodically 104 | unsigned long currentTime = millis(); 105 | if (currentTime - lastFetchTime >= fetchInterval) { 106 | lastFetchTime = currentTime; 107 | fetchDataAndProcess(); 108 | } 109 | 110 | // Small delay to yield to background processes 111 | delay(10); 112 | } 113 | 114 | // ======================= Helper Functions ======================= 115 | 116 | // Logging function with timestamp 117 | void logSerial(const String& message) { 118 | Serial.print("["); 119 | Serial.print(millis()); 120 | Serial.print(" ms] "); 121 | Serial.println(message); 122 | } 123 | 124 | // Turn off all LEDs 125 | void turnOffLEDs() { 126 | digitalWrite(RED_LED_PIN, LOW); 127 | digitalWrite(GREEN_LED_PIN, LOW); 128 | digitalWrite(BLUE_LED_PIN, LOW); 129 | logSerial("All LEDs are now OFF."); 130 | } 131 | 132 | // Blink a single LED 133 | void blinkLED(uint8_t pin, unsigned long delayTime, int times) { 134 | for (int i = 0; i < times; i++) { 135 | digitalWrite(pin, HIGH); 136 | delay(delayTime); 137 | digitalWrite(pin, LOW); 138 | delay(delayTime); 139 | } 140 | logSerial(String("Blinked LED on pin ") + String(pin) + " " + String(times) + " times."); 141 | } 142 | 143 | // Blink two LEDs simultaneously 144 | void blinkLEDs(uint8_t pin1, uint8_t pin2, unsigned long delayTime, int times) { 145 | for (int i = 0; i < times; i++) { 146 | digitalWrite(pin1, HIGH); 147 | digitalWrite(pin2, HIGH); 148 | delay(delayTime); 149 | digitalWrite(pin1, LOW); 150 | digitalWrite(pin2, LOW); 151 | delay(delayTime); 152 | } 153 | logSerial(String("Blinked LEDs on pins ") + String(pin1) + " and " + String(pin2) + " " + String(times) + " times."); 154 | } 155 | 156 | // Set an LED to a solid state (ON/OFF) 157 | void setSolidLED(uint8_t pin, bool state) { 158 | digitalWrite(pin, state ? HIGH : LOW); 159 | } 160 | 161 | // Open the gate with specified cycles 162 | void openGate(int cycles) { 163 | logSerial("Starting gate operation..."); 164 | 165 | for (int i = 0; i < cycles; i++) { 166 | openTerminal(); 167 | 168 | clickOpenGateRapidly(); 169 | 170 | // Re-activate GATE_PIN_2 multiple times with delays 171 | for (int j = 0; j < 4; j++) { 172 | waitForDelay(4900); 173 | reActivateGatePin2(); 174 | } 175 | 176 | closeTerminal(); 177 | 178 | // Delay between cycles 179 | delay(2500); 180 | } 181 | 182 | logSerial("Gate operation completed."); 183 | } 184 | 185 | // Open the terminal; upon a click, the terminal will be open for 30 seconds 186 | void openTerminal() { 187 | digitalWrite(GATE_PIN_1, HIGH); 188 | delay(200); 189 | digitalWrite(GATE_PIN_1, LOW); 190 | logSerial("Terminal opened."); 191 | } 192 | 193 | // Click open gate rapidly three times 194 | void clickOpenGateRapidly() { 195 | digitalWrite(GATE_PIN_2, HIGH); 196 | delay(100); 197 | digitalWrite(GATE_PIN_2, LOW); 198 | delay(300); 199 | 200 | digitalWrite(GATE_PIN_2, HIGH); 201 | delay(150); 202 | digitalWrite(GATE_PIN_2, LOW); 203 | delay(350); 204 | 205 | digitalWrite(GATE_PIN_2, HIGH); 206 | delay(200); 207 | digitalWrite(GATE_PIN_2, LOW); 208 | delay(400); 209 | 210 | logSerial("Gate clicked rapidly."); 211 | } 212 | 213 | // Wait for specified duration in milliseconds 214 | void waitForDelay(unsigned long duration) { 215 | logSerial("Waiting for " + String(duration) + " milliseconds..."); 216 | delay(duration); 217 | } 218 | 219 | // Re-activate GATE_PIN_2 to keep the gate open 220 | void reActivateGatePin2() { 221 | digitalWrite(GATE_PIN_2, HIGH); 222 | delay(150); 223 | digitalWrite(GATE_PIN_2, LOW); 224 | logSerial("Re-activated GATE_PIN_2."); 225 | } 226 | 227 | // Close the terminal so that in the next loop the terminal will be opened again 228 | void closeTerminal() { 229 | digitalWrite(GATE_PIN_1, HIGH); 230 | delay(250); 231 | digitalWrite(GATE_PIN_1, LOW); 232 | logSerial("Terminal closed."); 233 | } 234 | 235 | // Connect to WiFi with retries 236 | void connectToWiFi() { 237 | logSerial("Connecting to WiFi: " + String(ssid)); 238 | WiFi.begin(ssid, password); 239 | 240 | // Indicate connection attempt with blue LED blinking 241 | blinkLED(BLUE_LED_PIN, 250, 6); // 6 blinks (approx. 3 seconds) 242 | 243 | int retries = 0; 244 | while (WiFi.status() != WL_CONNECTED && retries < 20) { // total wait up to 10 seconds 245 | delay(500); 246 | Serial.print("."); // Keeping this separate for continuity 247 | // Blink blue LED during connection attempt 248 | digitalWrite(BLUE_LED_PIN, HIGH); 249 | delay(100); 250 | digitalWrite(BLUE_LED_PIN, LOW); 251 | delay(100); 252 | retries++; 253 | } 254 | 255 | if (WiFi.status() == WL_CONNECTED) { 256 | logSerial("Connected to WiFi network."); 257 | logSerial("IP Address: " + WiFi.localIP().toString()); 258 | // Indicate successful connection with a single blue blink 259 | blinkLED(BLUE_LED_PIN, 200, 1); 260 | } else { 261 | logSerial("Failed to connect to WiFi."); 262 | // Indicate failure with red LED flashing 263 | setSolidLED(RED_LED_PIN, true); 264 | delay(500); 265 | setSolidLED(RED_LED_PIN, false); 266 | } 267 | } 268 | 269 | // Handle button press with debouncing 270 | void handleButtonPress() { 271 | int buttonState = digitalRead(BUTTON_PIN); 272 | unsigned long currentTime = millis(); 273 | 274 | if (buttonState != lastButtonState) { 275 | lastDebounceTime = currentTime; 276 | } 277 | 278 | if ((currentTime - lastDebounceTime) > debounceDelay) { 279 | if (buttonState == LOW) { // Button pressed 280 | logSerial("Button pressed! Initiating gate opening."); 281 | openGate(6); 282 | // Add LED indication for button press 283 | setSolidLED(GREEN_LED_PIN, true); 284 | delay(500); 285 | setSolidLED(GREEN_LED_PIN, false); 286 | } 287 | } 288 | 289 | lastButtonState = buttonState; 290 | } 291 | 292 | // Fetch data from server and process it 293 | void fetchDataAndProcess() { 294 | // Indicate start of data fetch with blue LED 295 | setSolidLED(BLUE_LED_PIN, true); 296 | 297 | logSerial("Connecting to server: " + String(host)); 298 | 299 | // Create a secure client 300 | WiFiClientSecure client; 301 | client.setInsecure(); // WARNING: Skips SSL certificate verification 302 | 303 | // Attempt to connect to the server 304 | unsigned long connectStartTime = millis(); 305 | if (!client.connect(host, httpsPort)) { 306 | logSerial("Connection to server failed!"); 307 | // Indicate connection failure with red LED 308 | setSolidLED(BLUE_LED_PIN, false); 309 | setSolidLED(RED_LED_PIN, true); 310 | delay(500); 311 | setSolidLED(RED_LED_PIN, false); 312 | return; 313 | } 314 | unsigned long connectEndTime = millis(); 315 | logSerial("Connected to server. Connection time: " + String(connectEndTime - connectStartTime) + " ms"); 316 | 317 | // Formulate the GET request 318 | String getRequest = String("GET ") + url + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n"; 319 | 320 | logSerial("Sending GET request:\n" + getRequest); 321 | 322 | // Send the GET request 323 | client.print(getRequest); 324 | 325 | // Read the status line 326 | String statusLine = client.readStringUntil('\n'); 327 | logSerial("Status Line: " + statusLine); 328 | 329 | // Parse the status code 330 | int statusCode = 0; 331 | int firstSpace = statusLine.indexOf(' '); 332 | int secondSpace = statusLine.indexOf(' ', firstSpace + 1); 333 | if (firstSpace > 0 && secondSpace > firstSpace) { 334 | String codeStr = statusLine.substring(firstSpace + 1, secondSpace); 335 | statusCode = codeStr.toInt(); 336 | } 337 | 338 | if (statusCode != 200) { 339 | logSerial("HTTP request failed with status code: " + String(statusCode)); 340 | setSolidLED(BLUE_LED_PIN, false); // Turn off blue LED as fetch failed 341 | setSolidLED(RED_LED_PIN, true); 342 | delay(500); 343 | setSolidLED(RED_LED_PIN, false); 344 | return; 345 | } 346 | 347 | // Variables to store Content-Length if present 348 | long contentLength = -1; 349 | 350 | // Read headers 351 | while (client.connected()) { 352 | String line = client.readStringUntil('\n'); 353 | if (line == "\r") { 354 | break; 355 | } 356 | 357 | // Check for Content-Length header 358 | if (line.startsWith("Content-Length:")) { 359 | contentLength = line.substring(strlen("Content-Length:")).toInt(); 360 | } 361 | } 362 | 363 | // Check Content-Length and handle accordingly 364 | if (contentLength <= 5) { 365 | logSerial("Content-Length is less than or equal to 5 bytes. Treating as empty response. Skipping processing."); 366 | 367 | // Turn off blue LED as processing is skipped 368 | setSolidLED(BLUE_LED_PIN, false); 369 | 370 | client.stop(); // Close the connection 371 | return; 372 | } 373 | logSerial("Content-Length: " + String(contentLength) + " bytes"); 374 | 375 | // Check if data is available 376 | if (!client.available()) { 377 | logSerial("No data received from server."); 378 | // Indicate no data with red LED blinking 379 | blinkLED(RED_LED_PIN, 500, 3); 380 | 381 | // Turn off blue LED as fetch failed 382 | setSolidLED(BLUE_LED_PIN, false); 383 | 384 | client.stop(); 385 | return; 386 | } 387 | 388 | // Read the JSON payload 389 | logSerial("Reading JSON payload..."); 390 | unsigned long payloadStartTime = millis(); 391 | String payload = ""; 392 | while (client.available()) { 393 | char c = client.read(); 394 | payload += c; 395 | } 396 | unsigned long payloadEndTime = millis(); 397 | logSerial("JSON Payload Received:"); 398 | logSerial(payload); 399 | logSerial("Time taken to read payload: " + String(payloadEndTime - payloadStartTime) + " ms"); 400 | 401 | // Turn off blue LED as response has been received 402 | setSolidLED(BLUE_LED_PIN, false); // Indicate end of data fetch 403 | 404 | client.stop(); // Close the connection 405 | 406 | // Remove BOM if present 407 | if (payload.length() >= 3) { 408 | uint8_t firstByte = payload[0]; 409 | uint8_t secondByte = payload[1]; 410 | uint8_t thirdByte = payload[2]; 411 | if (firstByte == 0xEF && secondByte == 0xBB && thirdByte == 0xBF) { 412 | logSerial("BOM detected. Removing BOM..."); 413 | payload.remove(0, 3); 414 | logSerial("BOM removed. Updated Payload:"); 415 | logSerial(payload); 416 | } 417 | } 418 | 419 | // Check if payload is not empty 420 | if (payload.length() == 0) { 421 | logSerial("Payload is empty after BOM removal."); 422 | // Indicate empty payload 423 | blinkLED(RED_LED_PIN, 500, 3); 424 | return; 425 | } 426 | 427 | // Parse the JSON 428 | logSerial("Parsing JSON..."); 429 | 430 | // Define a capacity based on expected JSON size 431 | const size_t capacity = JSON_OBJECT_SIZE(5) + JSON_ARRAY_SIZE(1) + 200; 432 | DynamicJsonDocument doc(capacity); 433 | 434 | unsigned long parseStartTime = millis(); 435 | // Deserialize the JSON 436 | DeserializationError error = deserializeJson(doc, payload); 437 | unsigned long parseEndTime = millis(); 438 | 439 | if (error) { 440 | logSerial("JSON parsing failed: " + String(error.c_str())); 441 | logSerial("Time taken to parse JSON: " + String(parseEndTime - parseStartTime) + " ms"); 442 | // Indicate JSON parsing failure with red LED blinking 443 | blinkLED(RED_LED_PIN, 500, 3); 444 | return; 445 | } 446 | 447 | logSerial("JSON parsing successful."); 448 | logSerial("Time taken to parse JSON: " + String(parseEndTime - parseStartTime) + " ms"); 449 | 450 | // Extract the 'data' array 451 | JsonArray data = doc["data"]; 452 | if (!data) { 453 | logSerial("No 'data' field found in JSON."); 454 | // Indicate missing 'data' field with blue LED blink 455 | blinkLED(BLUE_LED_PIN, 500, 1); 456 | return; 457 | } 458 | 459 | logSerial("'data' array found in JSON."); 460 | logSerial("Number of items in 'data': " + String(data.size())); 461 | 462 | if (data.size() == 0) { 463 | logSerial("'data' array is empty."); 464 | // Indicate empty 'data' array with red LED blinking 465 | blinkLED(RED_LED_PIN, 500, 3); 466 | return; 467 | } 468 | 469 | // Iterate through the 'data' array and search for the term 470 | bool found = false; 471 | logSerial("Searching for the term: " + String(searchTerm)); 472 | for (JsonVariant value : data) { 473 | if (value.is()) { 474 | String location = value.as(); 475 | logSerial("Checking location: " + location); 476 | if (location.indexOf(searchTerm) != -1) { 477 | found = true; 478 | logSerial("Search term found in location: " + location); 479 | break; 480 | } 481 | } else { 482 | logSerial("Non-string value encountered in 'data' array."); 483 | } 484 | } 485 | 486 | // Control LEDs and gate based on search results 487 | if (found) { 488 | logSerial("Search term '" + String(searchTerm) + "' found."); 489 | setSolidLED(GREEN_LED_PIN, true); // Turn on green LED 490 | logSerial("Green LED is ON."); 491 | openGate(6); 492 | setSolidLED(GREEN_LED_PIN, false); // Turn off green LED 493 | logSerial("Green LED is OFF."); 494 | } else { 495 | logSerial("Search term '" + String(searchTerm) + "' not found."); 496 | // Blink blue and red LEDs to indicate no match 497 | blinkLEDs(BLUE_LED_PIN, RED_LED_PIN, 500, 3); 498 | logSerial("Blue and Red LEDs blinked 3 times."); 499 | } 500 | } 501 | 502 | // Optional: Function to set RGB LED color (if applicable) 503 | void setRGBColor(int red, int green, int blue) { 504 | digitalWrite(RED_LED_PIN, red ? HIGH : LOW); 505 | digitalWrite(GREEN_LED_PIN, green ? HIGH : LOW); 506 | digitalWrite(BLUE_LED_PIN, blue ? HIGH : LOW); 507 | logSerial("Set RGB LED to R:" + String(red) + " G:" + String(green) + " B:" + String(blue) + "."); 508 | } 509 | --------------------------------------------------------------------------------