Category: IoT

  • How to Detect Gas with ESP32 and MQ-2 Sensor and Send Real-Time Mobile Alerts

    Introduction:

    In our previous blog, we demonstrated how to set up an AWS IoT Core account and control a relay using an ESP32. That project was a great starting point for cloud-connected home automation.

    In this tutorial, we will take it a step further by integrating an MQ-2 gas sensor with the ESP32 to detect harmful gases like LPG, methane, and smoke. The ESP32 will send these readings to AWS IoT Core, and our custom mobile application will provide real-time alerts, ensuring you are immediately notified of any potential hazards.

    By the end of this blog, you’ll have a working system that not only automates devices but also enhances home safety with instant mobile notifications.

    About MQ-2 Sensor:

    What gases it detects:

    The MQ-2 gas sensor is a low-cost and easy-to-use sensor that can detect a variety of gases commonly found in homes and workplaces. It is widely used for home safety and DIY IoT projects.

    What Gases the MQ-2 Sensor Can Detect:

    • LPG (Liquefied Petroleum Gas) – commonly used for cooking
    • Methane (CH₄) – found in natural gas and biogas
    • Butane (C₄H₁₀) – another fuel gas often found in lighters
    • Smoke – from combustion or fire sources
    • Hydrogen (H₂) – produced in some industrial processes
    • Alcohol vapors – volatile organic compounds
    • Propane (C₃H₈) – used in some household fuel systems

    ⚠️ Note:
    The MQ-2 sensor detects the presence of combustible gases like LPG, methane, smoke, and alcohol vapors, but it cannot tell which gas it is. It only gives a voltage output proportional to the overall gas concentration in the air. Perfect for DIY projects and experimentation, but not for precise gas identification.

    Hardware Requirements:

    o build this project, you will need the following components:

    ComponentPurpose
    ESP32 Development BoardActs as the main controller and connects to AWS IoT Core
    MQ-2 Gas Sensor ModuleDetects gases like LPG, methane, and smoke
    Relay ModuleUsed to control appliances remotely via AWS IoT
    Jumper WiresFor making connections between ESP32, sensor, and relay
    Power Supply5V for MQ-2 and relay, 3.3V logic for ESP32
    Breadboard (optional)For easy prototyping without soldering

    Circuit Connections:

    MQ-2 Gas Sensor → ESP32

    📌 Safety Note for MQ‑2 Gas Sensor

    In many hobby projects, the MQ‑2 gas sensor is connected directly to the ESP32 analog input pin for simplicity.
    While this works in many cases, it is not technically safe long-term because the MQ‑2 outputs up to 5V analog signals, and ESP32 ADC pins are designed for maximum 3.3V input.
    Direct connection can lead to:

    • Gradual damage to ESP32 input pins.
    • Inaccurate sensor readings.
    • Reduced reliability over time.

    Recommended: Use a simple voltage divider or logic level shifter to scale the MQ‑2 analog output down to safe levels for the ESP32.

    • VCC → 5V (power for heating element)
    • GND → GND
    • A0 → GPIO34 (analog input for reading gas level, use a 5v to 3.3 level shifter)

    Relay Module → ESP32 (from previous blog)

    • VCC → 5V
    • GND → GND
    • IN → GPIOXX (same GPIO as used previously for relay control)

    Tip: Keep the MQ-2 sensor powered for a few minutes before taking readings to allow it to warm up for accurate measurements.

    Firmware Logic:

    We take the average of 50 ADC samples from the MQ-2 sensor to ensure stable and reliable readings. This averaged sensor data is then published to AWS IoT Core on the topic:

    $aws/things/esp32/sensor/mq2sensor
    

    Alongside this, light control messages are also published to:

    $aws/things/esp32/devices/light
    

    For details on how to configure AWS IoT Core and set up publishing and subscribing to topics, please refer to our previous article.

    #include <WiFi.h>
    #include <WiFiClientSecure.h>
    #include <PubSubClient.h>
    #include <ArduinoJson.h>
    
    #define MQ2_PIN 34  // ADC pin
    #define LIGHT 13
    
    // -------- WiFi ----------
    const char* WIFI_SSID = "CHANGE_ME";
    const char* WIFI_PASSWORD = "CHANGE_ME";
    
    // -------- AWS IoT ----------
    const char* AWS_IOT_ENDPOINT = "CHANGE_ME";  // your AWS IoT Core endpoint
    const int AWS_IOT_PORT = 8883;
    
    // Certificates (download from AWS IoT Core)
    const char* AWS_CERT_CA = R"EOF(
    -----BEGIN CERTIFICATE-----
    Replace with your CA content
    -----END CERTIFICATE-----
    )EOF";
    
    const char* AWS_CERT_CRT = R"EOF(
    -----BEGIN CERTIFICATE-----
    Replace with your Certificate content
    -----END CERTIFICATE-----
    )EOF";
    
    const char* AWS_CERT_PRIVATE = R"EOF(
    -----BEGIN RSA PRIVATE KEY-----
    Replace with your Private Key
    -----END RSA PRIVATE KEY-----
    )EOF";
    
    // -------- Global Variables ----------
    WiFiClientSecure net;
    PubSubClient client(net);
    int mq2Threshold = 200;
    
    // -------- Functions ----------
    
    // Read MQ2 sensor with averaging
    int getMQ2Reading(int samples = 50) {
        long sum = 0;
        for (int i = 0; i < samples; i++) {
            sum += analogRead(MQ2_PIN);
            delay(2);
        }
        return sum / samples;
    }
    
    // Handle incoming JSON threshold update
    void handleThresholdMessage(char* message) {
        StaticJsonDocument<200> doc;
        DeserializationError error = deserializeJson(doc, message);
    
        if (error) {
            Serial.print("JSON parse failed: ");
            Serial.println(error.f_str());
            return;
        }
    
        if (doc.containsKey("threshold")) {
            mq2Threshold = doc["threshold"];
            Serial.print("Threshold updated: ");
            Serial.println(mq2Threshold);
        }
    }
    
    // Publish JSON payload to MQTT
    void publishMessage(const char* topic, const String& payload) {
        client.publish(topic, payload.c_str());
        Serial.printf("Published [ %s ] %s\n", topic, payload.c_str());
    }
    
    // Handle device commands from MQTT
    void handle_mqtt_msg(char* topic, byte* payload, unsigned int length) {
        if (strstr(topic, "$aws/things/esp32/devices/light")) {
            if (strstr((const char*)payload, "light-on")) {
                digitalWrite(LIGHT, LOW);
                Serial.println("LIGHT ON");
            } else if (strstr((const char*)payload, "light-off")) {
                digitalWrite(LIGHT, HIGH);
                Serial.println("LIGHT OFF");
            } else {
                Serial.println("LIGHT INVALID COMMAND");
            }
        } else if (strstr(topic, "$aws/things/esp32/sensor/mq2sensor")) {
            handleThresholdMessage((char*)payload);
        }
    }
    
    // Prepare sensor data as JSON string
    String prepareSensorData(int value) {
        StaticJsonDocument<200> doc;
        doc["value"] = value;
        String payload;
        serializeJson(doc, payload);
        return payload;
    }
    
    // MQTT callback
    void messageHandler(char* topic, byte* payload, unsigned int length) {
        Serial.print("<< ");
        Serial.print(topic);
        Serial.print(" ");
        for (unsigned int i = 0; i < length; i++) {
            Serial.print((char)payload[i]);
        }
        Serial.println();
        handle_mqtt_msg(topic, payload, length);
    }
    
    // Connect to AWS IoT Core
    void connectAWS() {
        net.setCACert(AWS_CERT_CA);
        net.setCertificate(AWS_CERT_CRT);
        net.setPrivateKey(AWS_CERT_PRIVATE);
    
        client.setServer(AWS_IOT_ENDPOINT, AWS_IOT_PORT);
        client.setCallback(messageHandler);
        client.setBufferSize(4096);
    
        Serial.print("Connecting to AWS IoT Core...");
        while (!client.connected()) {
            client.connect("esp32-gas-sensor");
            Serial.print(".");
            delay(1000);
        }
        client.subscribe("$aws/things/esp32/sensor/mq2sensor");
        Serial.println(" connected!");
    }
    
    void setup() {
        Serial.begin(115200);
        pinMode(LIGHT, OUTPUT);
        
        // Connect to WiFi
        WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
        Serial.print("Connecting to WiFi...");
        while (WiFi.status() != WL_CONNECTED) {
            Serial.print(".");
            delay(1000);
        }
        Serial.println(" connected!");
    
        // Sensor warm-up
        Serial.println("Warming up sensor...");
        for (int i = 120; i > 0; i--) {
            Serial.printf("Time left: %ds\r", i);
            delay(1000);
        }
        Serial.println("\nSensor is ready!");
    
        connectAWS();
    }
    
    void loop() {
        int sensorValue = getMQ2Reading();
        int mappedValue = map(sensorValue, 0, 4095, 0, 1024);
    
        static unsigned long lastTime = 0;
        unsigned long currentTime = millis();
    
        if (currentTime - lastTime > 5000) {  // every 5 seconds
            lastTime = currentTime;
            Serial.printf("MQ2 ADC Reading: %d\n", mappedValue);
            publishMessage("$aws/things/esp32/sensor/mq2sensor", prepareSensorData(mappedValue));
        }
    
        if (!client.connected()) {
            connectAWS();
        }
    
        client.loop();
    }
    

    Hands-on Testing with AWS IoT Core Client:

    Hardware Setup
    Connect the MQ-2 sensor as shown in the above circuit diagram.

    Upload Firmware
    Flash the ESP32 firmware after updating your Wi-Fi credentials, AWS endpoint, and certificates.

    Monitor Output
    Open the Serial Monitor or Serial Plotter in Arduino IDE.

    In normal conditions, you should see ADC readings in the range of 100–200.

    Now bring a gas lighter close to the sensor and press the button. You will notice the ADC readings rise sharply.

    Threshold & AWS Publish
    Once the reading crosses the default threshold, the ESP32 will publish a message to AWS IoT Core.
    (Refer to the figure below for the AWS IoT Core dashboard output).

    👉 For AWS IoT Core certificate and policy setup, please refer to our previous blog.
    👉 In the next part, we will cover Android App configuration and implementation for receiving notifications.

  • Beginner’s Guide to AWS IoT Core: Connect, Manage & Secure IoT Devices

    Introduction

    In this guide, you’ll learn how to connect an ESP32-WROOM-32 (38 Pin) development board to AWS IoT Core using the MQTT protocol. You’ll also control a 2-Channel Relay Module with optocoupler to switch devices remotely via the cloud. This is a perfect starting point for building smart home systems, industrial IoT projects, or remote device management solutions.

    By the end of this tutorial, you will:

    • Set up AWS IoT Core for secure device communication.
    • Program the ESP32 to communicate via MQTT.
    • Control relays connected to the ESP32 from AWS.

    Prerequisites

    Before you get started with AWS IoT Core and ESP32, make sure you have the following:

    Creating and Configuring a Thing in AWS IoT Core:

    • Basic Knowledge of MQTT Protocol
      Understanding of MQTT topics, publish/subscribe model, and message handling.
    • ESP32-WROOM-32 (38 Pin) Development Board
      A compatible ESP32 board for connecting to AWS IoT Core.
    • 2-Channel Relay Module with Optocoupler
      To control external devices or appliances via the ESP32.
    • Active AWS Account
      Necessary for creating IoT Things, certificates, and policies on AWS IoT Core.
    • Latest Arduino IDE Installed
      Required for writing, compiling, and uploading code to the ESP32 board.

    Start by logging into your AWS Management Console with the root user credentials. This guide assumes that your AWS account has already been created.

    If you’re a new user and need help setting up an AWS account from scratch, we’ll cover that in a detailed article soon.
    🎥 Meanwhile, follow this video to quickly create your AWS account step by step!

    Search for IoT Core:

    Select AWS IoT Core and Click on Things:

    Click on create things:

    Click on create single thing:

    Understanding AWS IoT Shadows: No Shadow, Named Shadow, and Unnamed (Classic) Shadow

    When you create a Thing in AWS IoT Core, you can choose how its state (shadow) will be managed. Device Shadows allow AWS IoT Core to store the device’s last reported and desired states, even when the device is offline. Here’s a quick overview of the available options:

    1. No Shadow

    If you select this option, AWS IoT Core will not maintain any shadow document for the device. The device must handle state management entirely through direct communication (such as MQTT messages). This is suitable for devices that do not require state persistence.

    2. Unnamed (Classic) Shadow

    Also known as the default shadow, the unnamed or classic shadow provides a single JSON document that represents the entire device state. This is ideal for simpler devices where one shadow is enough to reflect the device’s status, commands, and configuration.

    3. Named Shadow

    Named shadows allow you to create multiple shadow documents for the same Thing. This is useful for complex devices with multiple components — for example, a smart home device that manages both a thermostat and a light system can have separate named shadows for each component.

    For This Tutorial: Selecting ‘No Shadow’ for Simplicity

    In this guide, we will choose the ‘No Shadow’ option to keep the setup simple. This means AWS IoT Core will not maintain a Device Shadow for our Thing, and all device state management will rely on direct communication (such as MQTT messages). This approach is suitable for basic use cases or when shadow functionality is not required.

    Assigning a Thing Name and Keeping Default Settings

    For this example, set the Thing Name as “ESP32” to represent your IoT device. Leave all other settings at their default values to simplify the configuration process. This ensures a quick and hassle-free setup, especially suitable for beginners or testing purposes.

    Generate Certificates with Default Settings

    In the next step, choose the option to automatically generate a new certificate and keep all other settings at their default values. AWS IoT Core will create a certificate and private key pair for your Thing, which is essential for secure device authentication and communication.

    After the certificates are generated, be sure to download them for future use, as they won’t be accessible later.

    Skip Policy Attachment for Now and Create the Thing

    For this tutorial, we will not attach any policy at this stage. Policies define permissions for your device, but we will create and attach a policy separately in a later step.

    Simply click on “Create Thing” to complete the process. Your AWS IoT Thing (named ESP32) will now be created with default settings and a generated certificate.

    Download All Certificates for Future Use

    Once the Thing is created, make sure to download all the generated certificates and the private key file. These files are essential for establishing a secure connection between your ESP32 device and AWS IoT Core. Without these certificates, the ESP32 will not be able to authenticate and communicate with AWS IoT services.

    Creating an AWS IoT Policy for ESP32

    When creating the policy, use the following JSON policy document. This policy grants the required permissions to your ESP32 device for connecting, publishing, subscribing, and receiving messages in AWS IoT Core:

    In left side panel go to Security->Policies->Create Policy and select JSON

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": [
            "iot:Connect",
            "iot:Publish",
            "iot:Subscribe",
            "iot:Receive"
          ],
          "Resource": "*"
        }
      ]
    }
    

    ⚠️ Important:
    This policy provides full access to AWS IoT Core resources ("Resource": "*"). For production environments, it’s recommended to restrict this to specific IoT Things or topics to ensure better security.

    Attach the Policy to the Thing’s Certificate

    Now that the IoT Policy has been created, the next step is to attach it to the certificate associated with your ESP32 Thing. This allows your device to securely communicate with AWS IoT Core using the permissions defined in the policy.

    Steps to Attach the Policy:

    1. Go to the AWS IoT Core Console.
    2. In the left sidebar, navigate to Security → Certificates.
    3. Find the certificate you created earlier (associated with the ESP32 Thing).
    4. Click on the certificate ID to open its details.
    5. Under “Actions”, click “Attach policy”.
    6. From the list, select the policy you just created (e.g., ESP32_Policy) and click “Attach”.

    Now your ESP32 certificate is authorized to connect and interact with AWS IoT Core.

    Note Down the Device Endpoint

    Carefully note down the AWS IoT Device Endpoint URL, as this will be required later when configuring your ESP32 to securely connect to AWS IoT Core.

    You can find this endpoint in the AWS IoT Core Console under AWS Console → Connect -> Domain Configurations

    🔌 Connecting 5V Relay Module to ESP32 (Using GPIO13)

    ESP32 PinRelay Module PinFunction
    V5 (5V)VCCPower supply for the relay module
    GPIO13 (G13)IN1Relay control input
    GNDGNDCommon ground connection

    Steps to Connect ESP32 to AWS IoT Core

    • const char WIFI_SSID[] = “Change This”;                                      
    • const char WIFI_PASSWORD[] = “Change This”;    
    • const char AWS_IOT_ENDPOINT[] = “Use this from previous step”;  //change this
    • // Amazon Root CA 1
    static const char AWS_CERT_CA[] PROGMEM = R"EOF(
    
    -----BEGIN CERTIFICATE-----
    
    Replace with AWS CA1
    
    -----END CERTIFICATE-----
    
    )EOF";
    • // Device Certificate //change this
    static const char AWS_CERT_CRT[] PROGMEM = R"KEY(
    
    -----BEGIN CERTIFICATE-----
    
    Replace with device certificate
    
    -----END CERTIFICATE-----
    
    )KEY";
    • // Device Private Key //change this
    static const char AWS_CERT_PRIVATE[] PROGMEM = R"KEY(
    
    -----BEGIN RSA PRIVATE KEY-----
    
    Replace with device private key
    
    -----END RSA PRIVATE KEY-----
    
    )KEY";

    Upload the ESP32 Sketch After Updating Required Details

    Use the following ESP32 Arduino sketch to connect your device to AWS IoT Core. Before uploading, make sure to update the placeholders with your specific AWS IoT credentials:

    • Wi-Fi SSID and Password
    • AWS IoT Endpoint URL
    • Root CA Certificate
    • Device Certificate
    • Private Key

    ⚠️ Important: Replace the placeholder values with the actual credentials and endpoint you obtained from AWS IoT Core.

    #include <Arduino.h>
    #include "WiFi.h"
    #include <WiFiClientSecure.h>
    #include <PubSubClient.h>
    #include <pgmspace.h>
    const char WIFI_SSID[] = "--------";                                             //change this
    const char WIFI_PASSWORD[] = "-------";                                      //change this
    const char AWS_IOT_ENDPOINT[] = "--------";  //change this
    
    // Amazon Root CA 1
    static const char AWS_CERT_CA[] PROGMEM = R"EOF(
    -----BEGIN CERTIFICATE-----
    
    -----END CERTIFICATE-----
    )EOF";
    
    // Device Certificate                                               //change this
    static const char AWS_CERT_CRT[] PROGMEM = R"KEY(
    -----BEGIN CERTIFICATE-----
    
    -----END CERTIFICATE-----
     )KEY";
    
    // Device Private Key                                               //change this
    static const char AWS_CERT_PRIVATE[] PROGMEM = R"KEY(
    -----BEGIN RSA PRIVATE KEY-----
    
    -----END RSA PRIVATE KEY-----
     
     
    )KEY";
    
    #define THING_NAME "ESP32"
    #define MQTT_PACKET_SIZE 1024
    WiFiClientSecure net = WiFiClientSecure();
    PubSubClient client(net);
    #define LIGHT 13
    const char publishShadowUpdate[] = "$aws/things/" THING_NAME "/shadow/update";
    char publishPayload[MQTT_PACKET_SIZE];
    void updateThing() {
      strcpy(publishPayload, "{\"state\": {\"reported\": {\"powerState\":\"ON\"}}}");
      publishMessage(publishShadowUpdate, publishPayload);
    }
    
    char *subscribeTopic[1] = {
      "$aws/things/" THING_NAME "/command/light"
    };
    
    void connectAWS() {
      int time_connect = millis();
      WiFi.mode(WIFI_STA);
      WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
      Serial.println("Connecting to Wi-Fi");
      while (WiFi.status() != WL_CONNECTED && millis() - time_connect < 60000) {
        Serial.print("#");
      }
    
      if (WiFi.status() != WL_CONNECTED && millis() - time_connect > 60000) {
        WiFi.disconnect();
        Serial.print("Disconnected WiFi ..");
        return;
      }
      configTime(19800, 0, "pool.ntp.org");  // For India time zone (GMT+5:30)
      while (time(nullptr) < 100000) {
        delay(100);
        Serial.print(".");
      }
      Serial.println("\nTime synchronized!");
      // Configure WiFiClientSecure to use the AWS IoT device credentials
      net.setCACert(AWS_CERT_CA);
      net.setCertificate(AWS_CERT_CRT);
      net.setPrivateKey(AWS_CERT_PRIVATE);
      // Connect to the MQTT broker on the AWS endpoint we defined earlier
      client.setServer(AWS_IOT_ENDPOINT, 8883);
    
      // Create a message handler
      client.setCallback(messageHandler);
      Serial.println("Connecting to AWS IOT");
      if (WiFi.status() == WL_CONNECTED)
        Serial.print("WiFi Connected!");
      while ((WiFi.status() == WL_CONNECTED) && (!client.connect(THING_NAME))) {
        Serial.print("*");
      }
    
      if (!client.connected()) {
        Serial.println("AWS IoT Timeout!");
        return;
      }
    
      // Subscribe to a topic
      for (int i = 0; i < 1; i++) {
        client.subscribe(subscribeTopic[i]);
        Serial.println(subscribeTopic[i]);
      }
      Serial.println("AWS IoT Connected!");
    }
    
    void publishMessage(const char *topic, char *payload) {
      client.publish(topic, payload);
      Serial.print("Published [ ");
      Serial.print(" ");
      Serial.print(topic);
      Serial.print("] ");
      Serial.println(payload);
    }
    
    void handle_mqtt_msg(char *topic, byte *payload, unsigned int length) {
      char resp[60] = { 0 }; 
      if (strstr(topic, "$aws/things/" THING_NAME "/command/light")) {
        if (strstr((const char *)payload, "light-on") != NULL) {
          digitalWrite(LIGHT, LOW);
          Serial.println("relay_update(LIGHT,LOW)");
        } else if (strstr((const char *)payload, "light-off") != NULL) {
          digitalWrite(LIGHT, HIGH);
          Serial.println("relay_update(LIGHT,HIGH)");
        } else {
          Serial.println("relay_update(LIGHT,INVALID)");
        }
      } else {
        Serial.println("Invalid: command");
      }
    }
    
    
    void messageHandler(char *topic, byte *payload, unsigned int length) {
      Serial.print("Message arrived [");
      Serial.print(topic);
      Serial.print("] ");
      for (int i = 0; i < length; i++) {
        Serial.print((char)payload[i]);
      }
      handle_mqtt_msg(topic, payload, length);
      Serial.println();
    }
    
    void wifi_setup() {
      connectAWS();
    }
    
    void setup() {
      pinMode(LIGHT, OUTPUT);
      digitalWrite(LIGHT, HIGH);
      delay(1000);
      Serial.begin(115200);
      while (!Serial) {
        ;  // wait for serial port to connect. Needed for native USB port only
      }
      delay(3000);
      wifi_setup();
    }
    
    void mqtt_task_update() {
    
      static uint32_t now, last_sent;
      if (!client.connected()) {
        connectAWS();
      }
      now = millis();
      if (now - last_sent > 1000 * 60) {
        char msg[60] = { 0 };  
        sprintf(msg, "{ \"message\": \"Hello from ESP 32\" }");
        publishMessage("MessageFromESP32", msg);
        last_sent = now;
      }
      client.loop();
    }
    void loop() {
      mqtt_task_update();
    }
    

    AWS IoT Topic and Message Structure for Relay Control

    MQTT TopicMessage PayloadAction Performed
    $aws/things/ESP32/command/lightlight-onTurns the relay (light) ON
    $aws/things/ESP32/command/lightlight-offTurns the relay (light) OFF

    To turn on the relay, send below command and message

    To turn off the relay send below command and message