Close
0%
0%

ESP32 & MQ2 GAZ DETECTION E.MAIL

Learn how to build a gas detector using an ESP32 and an MQ-2 sensor, featuring an email alert system through a PHP server.

Public Chat
Similar projects worth following
101 views
0 followers
Learn how to build a gas detector using an ESP32 and an MQ-2 sensor, featuring an email alert system through a PHP server and the sendmail method.

This project comes with highly commented source code to ensure clear understanding, even for beginners.

It's important to note that for safety reasons, this project should not be considered a reliable security device.

This project is designed with customization in mind, requiring minimal coding effort, thanks to its Access Point mode. This functionality launches a web server, providing a straightforward interface for the end-user to input their connection details directly into the NVS (Non-Volatile Storage). This method significantly simplifies the customization process.

The aim is to facilitate easy adjustments and personalizations to the gas detector system, enhancing user experience without the need for extensive programming.

  • 1 × ESP-WROOM-32 WIFI Bluetooth
  • 1 × MQ-2 Detection Smoke methane liquefied Gas Sensor Module
  • 1 × LCD display 16*2
  • 1 × Potentiometer
  • 1 × Push Button Switch

  • 1
    Wiring diagram.

    In this example, I'm using an older LCD screen that doesn't have I2C capability. As a result, the wiring process is more extensive and requires additional cables. Additionally, a potentiometer is needed to adjust the screen's brightness and contrast. If you have an LCD screen equipped with an I2C interface, you'll find that the wiring becomes much simpler.

    The MQ-2 sensor features two outputs: an analog output and a digital output. In this example, we utilize the analog signal to monitor the concentration of gas in the atmosphere and to test the sensor's responsiveness. The trigger threshold is set within the code, and this value can be easily adjusted to meet your specific requirements. If you choose to use the digital output, the detection threshold can be modified using the built-in potentiometer on the MQ-2 sensor.

    The button connected to GPIO17 pin acts as a pull-up resistor, which enables the deletion of data stored in the NVS (SSID and WiFi network password). This functionality is essential if there's an error in the entered information or if you need to update these details to connect the device to a different network, for example.

  • 2
    Update the PHP page and then upload it to your server.
    <?php
    // Define the expected API key
    $expectedApiKey = "7DNx2M1lyxUASVqN";
    
    // Check for the API key and the value in the POST data
    if (isset($_POST['apikey']) && $_POST['apikey'] === $expectedApiKey) {
        if(isset($_POST['value'])) {
            $value = $_POST['value'];
    
            if($value > 2000) {
                // Prepare the email
                $to = 'recipient@gmail.com'; // The recipient's email address
                $subject = 'MQ2 value alert'; // Mail's subject
                $message = 'The MQ2 value has exceeded 2000. Current value: ' . $value; // Content of the mail.
                $headers = 'From: sender@gmail.com' . "\r\n" .
                           'Reply-To: sender@gmail.com' . "\r\n" .
                           'X-Mailer: PHP/' . phpversion();
    
                // Send the email
                mail($to, $subject, $message, $headers);
    
                echo "Email sent for the value: " . $value;
            } else {
                echo "Value received: " . $value . " - No alert sent.";
            }
        } else {
            // If no value was sent
            echo "No data received.";
        }
    } else {
        // Access denied if the API key is wrong or not provided
        echo "Access denied: invalid API key.";
    }
    ?>
    
    

    Simply replace the recipient's and sender's addresses. Remember to change the APIKey for security reasons.

    Place your 'mq2_send_mail.php' page on your server and note:

    • The full URL of this page.
    • The APIKey you have mentioned.
  • 3
    Upload the code to the ESP32 board.

    This code is structured around FreeRTOS tasks, offering a practical example of its task management capabilities. Designed with students in mind, the code includes thorough comments to facilitate understanding and learning. To tailor this project to your specific needs, adjustments will be necessary in several areas:

    • Adjust the gas detection threshold, which is preset to 2000.
    • Update the API key for secure communication.
    • Change the URL to point to your designated PHP page.

    For debugging purposes, especially during email transmission, a serial connection is employed. This setup is instrumental in confirming the successful acceptance of requests by your PHP server, ensuring that your email alerts function as intended.

    // Include the WiFi library to enable WiFi connectivity features.
    #include <WiFi.h>
    
    // Include the WebServer library to create and manage a web server.
    #include <WebServer.h>
    
    // Include the Preferences library for saving and loading preferences in non-volatile storage (NVS).
    #include <Preferences.h>
    
    // Include the LiquidCrystal library to control LCD displays.
    #include <LiquidCrystal.h>
    
    // Include the HTTP  library to send the data to the sendmail.php
    #include <HTTPClient.h>
    
    /*
    Defines GPIO pin number 17 as the pin used for the WiFi reset button.
    Pressing this button clears the SSID and password from the Non-Volatile Storage (NVS), facilitating corrections to any errors
    made during the initial setup or enabling a switch to another WiFi network.
    */
    const int WIFI_RESET_BTN = 17;
    
    
    // This will be utilized later to send the value.
    volatile int globalMQ2Value = 0;
    
    // URL adress of the php page
    const char* serverName = "https://www.yourwebserveur.com/MQ2/mq2_send_mail.php";
    
    // Assigns GPIO 32 to be used for reading the analog signal from the MQ-2 sensor.
    const int MQ2_ANALOG_IN = 32;
    
    /*
    Using an I2C interface could simplify connections and code for LCD displays, but this example utilizes an older LCD screen
    that does not support I2C. Therefore, it directly interfaces with the ESP32 using multiple GPIO pins for control and data.
    */
    const int rs = 22, en = 21, d4 = 5, d5 = 18, d6 = 23, d7 = 19;
    LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
    
    /*
    Creates an instance of the Preferences class, which is used to store and retrieve key-value pairs in the non-volatile storage (NVS) of the ESP32.
    This is used for saving configurations and settings that need to persist across restarts, such as WiFi credentials.
    */
    Preferences preferences;
    
    /*
    Initializes an instance of the WebServer class on port 80, the default HTTP port. This sets up the ESP32 to act as a web server, allowing it to respond to HTTP requests.
    We will use it to create a web interfaces for device configuration.
    */
    WebServer server(80);
    
    //---------- TASK FOR READING AND DISPLAYING THE MQ-2 SENSOR VALUES.
    void readMQ2Task(void *parameter) {
    
      // Buffer to hold the display string including the MQ-2 value.
      char buffer[20];
    
      // Infinite loop for the task.
      for (;;) {
    
        // Reads the analog value from the MQ-2 sensor.
        int MQ2_ANALOG_VALUE = analogRead(MQ2_ANALOG_IN);
    
        // Update the global variable.
        globalMQ2Value = MQ2_ANALOG_VALUE;
    
        // Sets the cursor to the beginning of the second line.
        lcd.setCursor(0, 1);
    
        /* Formats the sensor value as a string, ensuring it occupies exactly 4 characters.
        This approach prevents display issues when transitioning from a value with more digits to fewer digits.
          - The "%-4d" is a format specifier for an integer (d represents an integer in decimal form).
          - The % sign introduces the format specification.
          - The - sign indicates left alignment.
          - The number 4 specifies the minimum field width. If the value to be displayed is less than 4 digits, it will be padded
            with spaces on the right due to the left alignment specified by "-".
        */
        sprintf(buffer, "MQ2 Value: %-4d", MQ2_ANALOG_VALUE);
    
        /* Prints the formatted string to the LCD. Using the buffer ensures that the
        display area is consistently used, avoiding the issue of leftover characters.
        */
        lcd.print(buffer);
    
        // Waits for one second before the next reading, allowing the display to update.
        delay(1000);
      }
    }
    
    
    //---------- TASK TO CONNECT TO WIFI ON STARTUP.
    void connectToWifiAndDisplay() {
    
      // Opens the "wifi" namespace in the NVS for both reading and writing.
      preferences.begin("wifi", true);
    
      // If SSID or password has not been set previously, the corresponding variable will be an empty string.
      String ssid = preferences.getString("ssid", "");
      String password = preferences.getString("password", "");
    
      // Closes the preferences to free resources, ensuring no further access to the stored preferences until next opened.
      preferences.end();
    
      if (!ssid.isEmpty()) {
        bool isConnected = false;
    
        for (int attempt = 0; attempt < 2 && !isConnected; attempt++) {
          WiFi.begin(ssid.c_str(), password.c_str());
    
          // Clears and prepares the display.
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print("Connecting to:");
          lcd.setCursor(0, 1);
          lcd.print(ssid);
    
          // Attempts to connect with a maximum of 20 tries per attempt.
          for (int attempts = 0; attempts < 20 && WiFi.status() != WL_CONNECTED; attempts++) {
    
            // Waits between each attempt.
            delay(500);
          }
    
          // Checks the connection status.
          if (WiFi.status() == WL_CONNECTED) {
            isConnected = true;
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print("WIFI OK");
          } else {
    
            // Clears and prepares the display.
            lcd.clear();
            lcd.setCursor(0, 0);
    
            // Prints "WIFI NOK" on first attempt, "WIFI ERROR" on second.
            lcd.print(attempt == 0 ? "WIFI NOK" : "WIFI ERROR");
    
            if (attempt == 0) {
              // Waits 1 minute before the second attempt.
              delay(60000);
            }
          }
        }
    
        // If the connection failed after two attempts.
        if (!isConnected) {
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print("WIFI ERROR");
        }
      } else {
    
        // Seems that SSID is not configured.
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("SSID UNKNOWN");
      }
    }
    
    //---------- TASK TO RUN THE WEB SERVER.
    void setupWebServer() {
    
      /*
      Sets up a route on the root ("/") path.
      When a GET request is received, it sends an HTML page for Wi-Fi configuration, including form fields for SSID and password, and a save button.
      */
      server.on("/", HTTP_GET, []() {
          server.send(200, "text/html",
          "<!DOCTYPE html>"
          "<html>"
          "<head>"
          "<title>Wi-Fi Configuration</title>"
          "<style>"
          "body { font-family: Arial, sans-serif; margin: 0; padding: 0; }"
          "h1 { color: #333; }"
          "form { max-width: 300px; margin: auto; padding: 20px; border: 1px solid #ccc; border-radius: 5px; }"
          "input[type='text'], input[type='password'] { width: 100%; padding: 10px; margin: 10px 0; border: 1px solid #ccc; border-radius: 5px; }"
          "input[type='submit'] { width: 100%; padding: 10px; border: none; border-radius: 5px; background-color: #007bff; color: white; }"
          "@media (max-width: 600px) {"
          "  form { width: 90%; }"
          "}"
          "</style>"
          "</head>"
          "<body>"
          "<h1>Wi-Fi Configuration</h1>"
          "<form action=\"/store\" method=\"post\">"
          "SSID: <input type=\"text\" name=\"ssid\"><br>"
          "Password: <input type=\"password\" name=\"password\"><br>"
          "<input type=\"submit\" value=\"Save\">"
          "</form></body></html>"
          );
      });
    
      /*
      Defines a route for "/store" to handle POST requests.
      If "ssid" and "password" arguments are present, it saves them to preferences.
      Then, it redirects to "/success". If arguments are missing, it responds with "Invalid Request".
      */
      server.on("/store", HTTP_POST, []() {
        if (server.hasArg("ssid") && server.hasArg("password")) {
          preferences.begin("wifi", false);
          preferences.putString("ssid", server.arg("ssid"));
          preferences.putString("password", server.arg("password"));
          // Closes the preferences to free resources, ensuring no further access to the stored preferences until next opened.
          preferences.end();
    
          server.sendHeader("Location", "/success", true);
          server.send(302, "text/plain", ""); // Redirection
        } else {
          server.send(400, "text/plain", "Invalid Request");
        }
      });
    
      /*
      Sets up a route for "/success" to respond to GET requests with an HTML page indicating successful Wi-Fi configuration.
      It includes a form with a button to submit a request to restart the ESP32.
      */
      server.on("/success", HTTP_GET, []() {
          String successPage = "<!DOCTYPE html>"
          "<html>"
          "<head>"
          "<title>Configuration Saved</title>"
          "<style>"
          "body { font-family: Arial, sans-serif; margin: 0; padding: 0; background-color: #f0f0f0; }"
          "h1 { color: #333; text-align: center; padding: 20px; }"
          "form { max-width: 300px; margin: auto; padding: 20px; border: 1px solid #ccc; border-radius: 5px; background-color: #fff; }"
          "input[type='submit'] { width: 100%; padding: 10px; border: none; border-radius: 5px; background-color: #007bff; color: white; cursor: pointer; }"
          "@media (max-width: 600px) {"
          "  form { width: 90%; }"
          "}"
          "</style>"
          "</head>"
          "<body>"
          "<h1>Configuration Successfully Saved!</h1>"
          "<form action=\"/restart\" method=\"post\">"
          "<input type=\"submit\" value=\"Restart the ESP32\">"
          "</form>"
          "</body></html>";
    
          server.send(200, "text/html", successPage);
      });
    
    
      /*
      Sets up a route for "/restart" to handle POST requests.
      When triggered, it sends a message indicating the ESP32 is restarting, then waits 1 second before executing the restart.
      Also marks the server to start listening for incoming connections.
      */
      server.on("/restart", HTTP_POST, []() {
        server.send(200, "text/plain", "Restarting the ESP32...");
        delay(1000);
        ESP.restart();
      });
      server.begin();
    }
    
    
    //---------- TASK TO RESET WIFI CONFIGURATION
    void WiFiResetTask(void *parameter) {
    
      for (;;) { // loop
        if (digitalRead(WIFI_RESET_BTN) == LOW) {
          delay(50); // Debounce delay.
          while (digitalRead(WIFI_RESET_BTN) == LOW) {
            delay(10); // Wait for the button to be released.
          }
    
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print("RESETTING WI-FI");
    
          preferences.begin("wifi", false);
          preferences.clear();
          // Closes the preferences to free resources, ensuring no further access to the stored preferences until next opened.
          preferences.end();
    
          delay(1000);
          ESP.restart();
        }
    
        /* If in AP mode (Access Point), continue handling web server clients.
        In this situation, the ESP32 is acting as an Access Point, allowing us to connect to it.
        This allows us to return to the web interface and fill out the form with the new or corrected SSID and password.
        It facilitates the configuration of the ESP32's Wi-Fi settings without the need for a direct serial or USB connection.
        */
        if (WiFi.getMode() & WIFI_MODE_AP) {
          server.handleClient();
        }
    
        // Delay to prevent task from running too frequently.
        vTaskDelay(100 / portTICK_PERIOD_MS);
      }
    }
    
    
    //---------- TASK TO CHECK WIFI CONNECTIVITY EVERY MINUTE.
    void wifiMonitorTask(void *parameter) {
    
      for(;;) { // Loop.
        preferences.begin("wifi", true);
        String ssid = preferences.getString("ssid", "");
        String password = preferences.getString("password", "");
        preferences.end();
    
        if (!ssid.isEmpty()) {
    
          // Overwrite the first line with spaces to "clear" it.
          lcd.setCursor(0, 0);
          for(int i = 0; i < 16; i++) {
            // Print a space at each position.
            lcd.print(" ");
          }
    
          lcd.setCursor(0, 0);
          WiFi.begin(ssid.c_str(), password.c_str());
          lcd.print("WIFI CHECK");
    
          int attempts = 0;
          while (WiFi.status() != WL_CONNECTED && attempts < 20) {
            delay(500);
            attempts++;
          }
    
          if (WiFi.status() == WL_CONNECTED) {
            // Overwrite the first line with spaces to "clear" it.
            lcd.setCursor(0, 0);
            for(int i = 0; i < 16; i++) {
              // Print a space at each position
              lcd.print(" ");
            }
    
            lcd.setCursor(0, 0);
            lcd.print("WIFI OK");
    
          } else {
    
            // Overwrite the first line with spaces to "clear" it.
            lcd.setCursor(0, 0);
            for(int i = 0; i < 16; i++) {
              // Print a space at each position
              lcd.print(" ");
            }
    
            lcd.setCursor(0, 0);
            lcd.print("WIFI NOK");
          }
        } else {
    
    // We leave it empty, because there is no WiFi access, so we go back to AP mode.
        }
    
        // Pauses the current task for one minute, allowing the scheduler to allocate processor time to other tasks.
        vTaskDelay(60000 / portTICK_PERIOD_MS);
      }
    }
    
    
    void sendValueTask(void * parameter){
      for(;;){
        if (WiFi.status() == WL_CONNECTED) { // Check if the WiFi connection is established.
          if(globalMQ2Value > 2000){
            Serial.println(globalMQ2Value);
            HTTPClient http;
            http.begin(serverName);
            http.addHeader("Content-Type", "application/x-www-form-urlencoded");
    
            String httpRequestData = "value=" + String(globalMQ2Value) + "&apikey=7DNx2M1lyxUASVqN"; // Prepare the data to send.
            int httpResponseCode = http.POST(httpRequestData); // Sending the data.
    
            if(httpResponseCode > 0){
            String response = http.getString(); // Get the response to the request
            Serial.println(httpResponseCode);   // Print the status code
            Serial.println(response);           // Print the response to the request
    
            } else {
              Serial.print("Error on sending POST: ");
              Serial.println(httpResponseCode);
            }
            http.end(); // Close the connection
            vTaskDelay(pdMS_TO_TICKS(60000)); // Delay the task execution for 1 minute.
    
          }
        } else {
          Serial.println("WiFi not connected. Waiting for connection...");
        }
        vTaskDelay(pdMS_TO_TICKS(10));
      }
    }
    
    
    void setup() {
    
      // For debugging purposes only
      Serial.begin(115200);
    
      // Initializes the LCD display
      lcd.begin(16, 2);
    
      pinMode(WIFI_RESET_BTN, INPUT_PULLUP);
    
      // Opens the "wifi" namespace in read-only mode to access stored preferences.
      preferences.begin("wifi", true);
    
      // Retrieves the value of the "ssid" key from preferences. If the key does not exist, returns an empty string.
      String ssid = preferences.getString("ssid", "");
    
      // Closes the preferences to free resources, ensuring no further access to the stored preferences until next opened.
      preferences.end();
    
      if (ssid.isEmpty()) {
    
        // If no SSID is configured, create a soft access point.
        WiFi.softAP("ESP32-GasMonitor", "azertyuiop");
    
        // Clears the LCD display and sets the cursor to the first line and column.
        lcd.clear();
        lcd.setCursor(0, 0);
    
        // Prints the IP address of the ESP32 soft AP on the LCD.
        lcd.print(WiFi.softAPIP());
    
        // Initializes the web server for configuration via the web interface.
        setupWebServer();
    
      } else {
        /*
        If an SSID is saved, attempt to connect to it and display the status.
        */
        connectToWifiAndDisplay();
      }
      /*
      Creates a new task dedicated to reading the MQ-2 gas sensor.
      This task is declared here because its readings need to be accessible outside of the task itself, ensuring that sensor data is available globally or to other parts of the program.
      Declaring it at this point sets it up early in the program's execution, making sure the sensor data is being processed and ready to be utilized as soon as the system needs it.
      */
      xTaskCreate(readMQ2Task, "Read MQ-2 Task", 2048, NULL, 1, NULL);
    
      // For the same reasons, we create a new task her for reading the reset button
      xTaskCreate(WiFiResetTask, "WiFi Reset Task", 2048, NULL, 1, NULL);
    
      // Task that checks the WiFi functionality every minute
      xTaskCreate(wifiMonitorTask, "WiFi Monitor Task", 2048, NULL, 1, NULL);
    
      xTaskCreate(
      sendValueTask,    /* Task function */
      "SendValueTask",  /* Name of task */
      10000,            /* Stack size of task */
      NULL,             /* Parameter of the task */
      1,                /* Priority of the task */
      NULL);            /* Task handle */
    }
    
    
    void loop() {
      // The loop is left empty because we are utilizing the multitasking environment provided by FreeRTOS.
      // All functionalities are managed through tasks defined in the setup or elsewhere.
    }
    

    Thank you for completing the customization process. You can now use and further enhance the ESP32/MQ-2 gas detection system to meet your specific needs.

     

View all 3 instructions

Enjoy this project?

Share

Discussions

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates