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.ino
#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.ino
#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.ino
#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
}