Iffat Jahan
CV

IoT · In the CV

IoT Gas and Fire Detection System

Developed a system that detects gas leaks and fire, then starts an alarm, exhaust fan and pump.

  • ESP32
  • Arduino
  • C++
  • ThingSpeak
  • MQ-2 gas and flame sensors

About

An ESP32-based system with gas and flame sensors, a servo motor and an LCD that detects fire and gas hazards. It sends data to ThingSpeak for remote monitoring, runs a pump and an exhaust fan based on the sensor readings, and turns on a buzzer and an LED when danger is detected.

How to run

  • nano.ino is the version for the Arduino Nano, merged_no.loadcell.ino is for the Arduino Uno, and espthingspeakbuttn.ino is for the ESP32.

Source code

espthingspeakbuttn.inoArduino sketch · 257 lines · 7.7 KB
#include <Wire.h>
#include <LiquidCrystal_PCF8574.h>
#include <ESP32Servo.h>
#include <WiFi.h>
#include <ThingSpeak.h>

// Initialize the LCD with the detected I2C address (0x3F)
LiquidCrystal_PCF8574 lcd(0x3F); // Replace 0x3F with your LCD's I2C address if different
Servo s1;

// Sensor and actuator pins
const int MQ2pin = 32;             // Gas sensor pin (ADC1_4)
const int flameSensorPin = 33;     // Flame sensor pin (ADC1_5)
const int buzzerPin = 25;          // Buzzer pin  
const int ledPin = 2;              // LED pin (Built-in LED on some ESP32 boards)

// Motor driver pins
const int IN1 = 26;  // Direction pin 1 for Motor 1 (pump)
const int IN2 = 27;  // Direction pin 2 for Motor 1
const int IN3 = 14;  // Direction pin 1 for Motor 2 (exhaust fan)
const int IN4 = 12;  // Direction pin 2 for Motor 2

// Wi-Fi and ThingSpeak configuration
const char *ssid = "Cuties";           // Your Wi-Fi SSID
const char *password = "soma2100606";  // Your Wi-Fi Password
unsigned long myChannelNumber =  2863341; // Your ThingSpeak channel number
const char *myWriteAPIKey = "PABDVCG239UFORMG"; // ThingSpeak Write API Key

WiFiClient client;

// Threshold values
const int GAS_THRESHOLD = 1000;     // Gas detection threshold
const int FLAME_THRESHOLD = 4000;   // Flame detection threshold

// Variables for blinking behavior
int currentState = 0;              // State variable for LCD timing
unsigned long previousMillis = 0;  // Timer for LCD timing
unsigned long buzzerMillis = 0;    // Timer for buzzer timing
bool buzzerState = false;          // Current state of the buzzer

// Field number for manual control of the servo
const int controlField = 3; // Field 3 for controlling the servo manually

// Setup function
void setup() {
  // Initialize Serial Monitor
  Serial.begin(9600);

  // Initialize Servo
  s1.attach(13); // Attach servo to GPIO13
  s1.write(0); // Start at 0 degree position (safe state)

  // Initialize pins
  pinMode(buzzerPin, OUTPUT);
  pinMode(ledPin, OUTPUT);

  // Motor pins as output
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(IN3, OUTPUT);
  pinMode(IN4, OUTPUT);

  // Set initial states for actuators
  digitalWrite(ledPin, LOW);
  digitalWrite(buzzerPin, LOW);

  // Stop both motors initially
  digitalWrite(IN1, LOW);
  digitalWrite(IN2, LOW);
  digitalWrite(IN3, LOW);
  digitalWrite(IN4, LOW);

  // Initialize LCD
  lcd.begin(16, 2); // Specify 16 columns and 2 rows
  lcd.setBacklight(255); // Turn on backlight

  // Connect to Wi-Fi
  connectToWiFi();

  // Initialize ThingSpeak
  ThingSpeak.begin(client);
}

// Function to handle the passive buzzer with alternating tones
void buzzerAlert() {
  unsigned long currentMillis = millis();
  if (currentMillis - buzzerMillis >= 500) { // Switch tone every 500ms
    buzzerMillis = currentMillis;
    if (buzzerState) {
      tone(buzzerPin, 1400); // Play 700Hz tone
    } else {
      tone(buzzerPin, 1800); // Play 800Hz tone
    }
    buzzerState = !buzzerState; // Toggle state
  }
}

// Function to stop the passive buzzer
void stopBuzzer() {
  noTone(buzzerPin); // Stop tone
}

// Function to control Motor 1 (pump)
void controlPumpMotor(bool state) {
  if (state) {
    digitalWrite(IN1, HIGH);
    digitalWrite(IN2, LOW);
  } else {
    digitalWrite(IN1, LOW);
    digitalWrite(IN2, LOW);
  }
}

// Function to control Motor 2 (exhaust fan)
void controlFanMotor(bool state) {
  if (state) {
    digitalWrite(IN3, HIGH);
    digitalWrite(IN4, LOW);
  } else {
    digitalWrite(IN3, LOW);
    digitalWrite(IN4, LOW);
  }
}

// Loop function
void loop() {
  // Read sensor values
  float gasSensorValue = analogRead(MQ2pin);
  float flameSensorValue = analogRead(flameSensorPin);

  // Log sensor values to Serial Monitor
  Serial.print("Gas Sensor: ");
  Serial.print(gasSensorValue);
  Serial.print("  Flame Sensor: ");
  Serial.println(flameSensorValue);

  // Update ThingSpeak with sensor values
  ThingSpeak.setField(1, gasSensorValue);    // Field 1 for Gas Sensor
  ThingSpeak.setField(2, flameSensorValue);  // Field 2 for Flame Sensor
  
  // Read the control button state from Field 3
  long controlValue = ThingSpeak.readField(myChannelNumber, controlField); // Field 3

  // Write to ThingSpeak
  int x = ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);
  if (x == 200) {
    Serial.println("Data updated successfully.");
  } else {
    Serial.println("Error updating data. HTTP error code: " + String(x));
  }

  // Check the control value and update the servo position
  if (controlValue == 1) {
    s1.write(180); // Turn servo to 180 degrees (ON)
  } else if (controlValue == 0) {
    s1.write(0);   // Turn servo to 0 degrees (OFF)
  }

  // Determine system state based on sensor readings
  bool gasDetected = gasSensorValue >= GAS_THRESHOLD;
  bool flameDetected = flameSensorValue <= FLAME_THRESHOLD;

  if (gasDetected || flameDetected) {
    // Gas or flame detected (or both)
    handleLCDTiming("WARNING! Gas/Fire", "     Alert!     ");
    s1.write(180); // Move servo to 180 degrees (danger state)
    controlFanMotor(true); // Turn on exhaust fan
    controlPumpMotor(true); // Turn on pump
    digitalWrite(ledPin, HIGH);
    buzzerAlert(); // Activate passive buzzer
  } else {
    // Nothing detected
    lcd.setCursor(0, 0);
    lcd.print("Everything is   ");
    lcd.setCursor(0, 1);
    lcd.print("SAFE and Fine   ");
    controlFanMotor(false); // Turn off exhaust fan
    controlPumpMotor(false); // Turn off pump
    digitalWrite(ledPin, LOW);
    stopBuzzer(); // Turn off the buzzer
    delay(500); // Keep the screen stable
  }

  delay(100); // Small delay for stability
}

// Function to handle first-row blinking/timing behavior
void handleLCDTiming(String firstRow, String secondRow) {
  unsigned long currentMillis = millis();

  switch (currentState) {
    case 0: // Show the first row for 1000 ms
      lcd.setCursor(0, 0);
      lcd.print(firstRow);
      lcd.setCursor(0, 1);
      lcd.print(secondRow);
      if (currentMillis - previousMillis >= 1000) { // Wait for 1000 ms
        previousMillis = currentMillis;
        lcd.setCursor(0, 0);
        lcd.print("                "); // Clear the first row
        currentState = 1;
      }
      break;
    case 1: // Turn off the first row for 300 ms
      if (currentMillis - previousMillis >= 300) { // Wait for 300 ms
        previousMillis = currentMillis;
        currentState = 2;
      }
      break;
    case 2: // Show the first row for 450 ms
      lcd.setCursor(0, 0);
      lcd.print(firstRow);
      lcd.setCursor(0, 1);
      lcd.print(secondRow);
      if (currentMillis - previousMillis >= 450) { // Wait for 450 ms
        previousMillis = currentMillis;
        lcd.setCursor(0, 0);
        lcd.print("                "); // Clear the first row
        currentState = 3;
      }
      break;
    case 3: // Turn off the first row for 200 ms
      if (currentMillis - previousMillis >= 200) { // Wait for 200 ms
        previousMillis = currentMillis;
        currentState = 4;
      }
      break;
    case 4: // Show the first row for 500 ms
      lcd.setCursor(0, 0);
      lcd.print(firstRow);
      lcd.setCursor(0, 1);
      lcd.print(secondRow);
      if (currentMillis - previousMillis >= 500) { // Wait for 500 ms
        previousMillis = currentMillis;
        lcd.setCursor(0, 0);
        lcd.print("                "); // Clear the first row
        currentState = 5;
      }
      break;
    case 5: // Turn off the first row for 300 ms
      if (currentMillis - previousMillis >= 300) { // Wait for 300 ms
        previousMillis = currentMillis;
        currentState = 0; // Reset to the first state
      }
      break;
  }
}

// Function to connect to Wi-Fi
void connectToWiFi() {
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }
  Serial.println("Connected to WiFi!");
}
merged_no.loadcell.inoArduino sketch · 192 lines · 5.5 KB
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Servo.h>

// Initialize the LCD with the detected I2C address (0x3F)
LiquidCrystal_I2C lcd(0x3F, 16, 2);
Servo s1;

// Sensor and actuator pins
const int MQ2pin = A3;             // Gas sensor pin
const int flameSensorPin = A0;     // Flame sensor pin
const int buzzerPin = 12;          // Buzzer pin
const int ledPin = 13;             // LED pin
const int fanPin = 6;              // Fan PWM pin
const int pumpPin = 5;             // Pump motor pin

// Threshold values
const int GAS_THRESHOLD = 350;     // Gas detection threshold
const int FLAME_THRESHOLD = 500;   // Flame detection threshold

// Variables for blinking behavior
int currentState = 0;              // State variable for LCD timing
unsigned long previousMillis = 0;  // Timer for LCD timing
unsigned long buzzerMillis = 0;    // Timer for buzzer timing
bool buzzerState = false;          // Current state of the buzzer

// Setup function
void setup() {
  // Initialize Serial Monitor
  Serial.begin(9600);

  // Initialize Servo
  s1.attach(10);
  s1.write(0);

  // Initialize pins
  pinMode(buzzerPin, OUTPUT);
  pinMode(ledPin, OUTPUT);
  pinMode(fanPin, OUTPUT);
  pinMode(pumpPin, OUTPUT);

  // Set initial states for actuators
  digitalWrite(ledPin, LOW);
  digitalWrite(buzzerPin, LOW);
  analogWrite(fanPin, 0);
  analogWrite(pumpPin, 0);

  // Initialize LCD
  lcd.init();
  lcd.backlight();
}

// Function to handle the passive buzzer with alternating tones
void buzzerAlert() {
  unsigned long currentMillis = millis();
  if (currentMillis - buzzerMillis >= 500) { // Switch tone every 500ms
    buzzerMillis = currentMillis;
    if (buzzerState) {
      tone(buzzerPin, 1500); // Play 1500Hz tone
    } else {
      tone(buzzerPin, 2000); // Play 2000Hz tone
    }
    buzzerState = !buzzerState; // Toggle state
  }
}

// Function to stop the passive buzzer
void stopBuzzer() {
  noTone(buzzerPin); // Turn off the buzzer
}

// Loop function
void loop() {
  // Read sensor values
  float gasSensorValue = analogRead(MQ2pin);
  float flameSensorValue = analogRead(flameSensorPin);

  // Log sensor values to Serial Monitor
  Serial.print("Gas Sensor: ");
  Serial.print(gasSensorValue);
  Serial.print("  Flame Sensor: ");
  Serial.println(flameSensorValue);

  // Determine system state based on sensor readings
  bool gasDetected = gasSensorValue >= GAS_THRESHOLD;
  bool flameDetected = flameSensorValue <= FLAME_THRESHOLD;

  if (gasDetected && flameDetected) {
    // Both gas and flame detected
    handleLCDTiming("WARNING:Gas,Fire", "     Alert!    ");
    s1.write(180);
    analogWrite(fanPin, 255);
    analogWrite(pumpPin, 255);
    digitalWrite(ledPin, HIGH);
    buzzerAlert(); // Activate passive buzzer
  } else if (gasDetected) {
    // Gas detected
    handleLCDTiming("    WARNING!    ", "Gas Leakage   ");
    s1.write(180);
    analogWrite(fanPin, 255);
    digitalWrite(ledPin, HIGH);
    buzzerAlert(); // Activate passive buzzer
  } else if (flameDetected) {
    // Flame detected
    handleLCDTiming("    WARNING!    ", "   Fire Alert  ");
    s1.write(180);
    analogWrite(pumpPin, 255);
    digitalWrite(ledPin, HIGH);
    buzzerAlert(); // Activate passive buzzer
  } else {
    // Nothing detected
    lcd.setCursor(0, 0);
    lcd.print("Everything is   ");
    lcd.setCursor(0, 1);
    lcd.print("SAFE and Fine   ");
    s1.write(0);
    analogWrite(fanPin, 0);
    analogWrite(pumpPin, 0);
    digitalWrite(ledPin, LOW);
    stopBuzzer(); // Turn off the buzzer
    delay(500); // Keep the screen stable
  }

  delay(100); // Small delay for stability
}

// Function to handle first-row blinking/timing behavior
void handleLCDTiming(String firstRow, String secondRow) {
  unsigned long currentMillis = millis();

  switch (currentState) {
    case 0: // Show the first row for 1000 ms
      lcd.setCursor(0, 0);
      lcd.print(firstRow);
      lcd.setCursor(0, 1);
      lcd.print(secondRow);
      if (currentMillis - previousMillis >= 1000) { // Wait for 1000 ms
        previousMillis = currentMillis;
        lcd.setCursor(0, 0);
        lcd.print("                "); // Clear the first row
        currentState = 1;
      }
      break;

    case 1: // Turn off the first row for 300 ms
      if (currentMillis - previousMillis >= 300) { // Wait for 300 ms
        previousMillis = currentMillis;
        currentState = 2;
      }
      break;

    case 2: // Show the first row for 450 ms
      lcd.setCursor(0, 0);
      lcd.print(firstRow);
      lcd.setCursor(0, 1);
      lcd.print(secondRow);
      if (currentMillis - previousMillis >= 450) { // Wait for 450 ms
        previousMillis = currentMillis;
        lcd.setCursor(0, 0);
        lcd.print("                "); // Clear the first row
        currentState = 3;
      }
      break;

    case 3: // Turn off the first row for 200 ms
      if (currentMillis - previousMillis >= 200) { // Wait for 200 ms
        previousMillis = currentMillis;
        currentState = 4;
      }
      break;

    case 4: // Show the first row for 500 ms
      lcd.setCursor(0, 0);
      lcd.print(firstRow);
      lcd.setCursor(0, 1);
      lcd.print(secondRow);
      if (currentMillis - previousMillis >= 500) { // Wait for 500 ms
        previousMillis = currentMillis;
        lcd.setCursor(0, 0);
        lcd.print("                "); // Clear the first row
        currentState = 5;
      }
      break;

    case 5: // Turn off the first row for 300 ms
      if (currentMillis - previousMillis >= 300) { // Wait for 300 ms
        previousMillis = currentMillis;
        currentState = 0; // Reset to the first state
      }
      break;
  }
}
nano.inoArduino sketch · 74 lines · 2.2 KB
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

// Initialize the LCD with the detected I2C address (0x3F)
LiquidCrystal_I2C lcd(0x3F, 16, 2);

// Sensor and actuator pins
const int MQ2pin = A3;             // Gas sensor pin
const int flameSensorPin = A0;     // Flame sensor pin
const int buzzerPin = 12;          // Buzzer pin
const int ledPin = 13;             // LED pin

// Motor driver pins
const int IN1 = 5;  // Direction pin 1 for Motor 1 (pump)
const int IN2 = 9;  // Direction pin 2 for Motor 1
const int IN3 = 3;  // Direction pin 1 for Motor 2 (exhaust fan)
const int IN4 = 11; // Direction pin 2 for Motor 2

// I2C Address for Arduino Nano (Slave)
const int slaveAddress = 8;  // Address of the Arduino Nano

// Variables for blinking behavior on LCD
int currentState = 0;              // State variable for LCD timing
unsigned long previousMillis = 0;  // Timer for LCD timing
unsigned long buzzerMillis = 0;    // Timer for buzzer timing
bool buzzerState = false;          // Current state of the buzzer

void setup() {
  // Initialize Serial Monitor
  Serial.begin(9600);

  // Initialize pins
  pinMode(buzzerPin, OUTPUT);
  pinMode(ledPin, OUTPUT);

  // Motor pins as output
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(IN3, OUTPUT);
  pinMode(IN4, OUTPUT);

  // Set initial states for actuators
  digitalWrite(ledPin, LOW);
  digitalWrite(buzzerPin, LOW);

  // Stop both motors initially
  digitalWrite(IN1, LOW);
  digitalWrite(IN2, LOW);
  digitalWrite(IN3, LOW);
  digitalWrite(IN4, LOW);

  // Initialize LCD
  lcd.init();
  lcd.backlight();

  // Start I2C as slave with address 8
  Wire.begin(slaveAddress);
  Wire.onRequest(requestEvent); // Register the requestEvent function
}

void loop() {
  // Nothing to do here, just waiting for requests from ESP32
}

void requestEvent() {
  int gasSensorValue = analogRead(MQ2pin);
  int flameSensorValue = analogRead(flameSensorPin);

  // Send sensor data to ESP32 (2 bytes for each sensor, gas and flame)
  Wire.write(gasSensorValue >> 8);  // Send high byte of gas value
  Wire.write(gasSensorValue & 0xFF); // Send low byte of gas value
  Wire.write(flameSensorValue >> 8);  // Send high byte of flame value
  Wire.write(flameSensorValue & 0xFF); // Send low byte of flame value
}