Cirkit Designer LogoCirkit DesignerYour all-in-one circuit design IDE Launch Cirkit Designer App

Arduino Nano-Based Air Quality Monitoring System with OLED Display and Alerts

View and Edit Project In Cirkit DesignerImage of Arduino Nano-Based Air Quality Monitoring System with OLED Display and Alerts

Circuit Documentation

Summary

This circuit is designed to monitor air quality using an MQ-7 gas sensor and provide visual and auditory alerts based on the detected gas levels. The system uses an Arduino Nano microcontroller to process sensor data and control various output devices, including a traffic light, a passive buzzer, a vibration motor, and an OLED display for real-time data visualization.

Component List

  1. Arduino Nano

    • Description: A compact microcontroller board based on the ATmega328P.
    • Pins: D1/TX, D0/RX, RESET, GND, D2, D3, D4, D5, D6, D7, D8, D9, D10, D11/MOSI, D12/MISO, VIN, 5V, A7, A6, A5, A4, A3, A2, A1, A0, AREF, 3V3, D13/SCK
  2. Traffic Light

    • Description: A module with three LEDs (Green, Yellow, Red) to indicate different levels of gas concentration.
    • Pins: Green, Yellow, Red, GND
  3. MQ-7 Breakout

    • Description: A gas sensor module for detecting carbon monoxide (CO) levels.
    • Pins: VCC, GND, DO, AO
  4. Passive Buzzer

    • Description: A simple buzzer for generating sound alerts.
    • Pins: S output signal, +VCC, GND
  5. PWM Vibration Motor Sensor Module Switch

    • Description: A module with a vibration motor for tactile feedback.
    • Pins: Signal, Vcc, Ground
  6. 128x64 OLED Display (I2C IIC SPI Serial)

    • Description: A small OLED display for showing sensor data and status messages.
    • Pins: GND, SDA, SCL, VCC

Wiring Details

Arduino Nano

Traffic Light

MQ-7 Breakout

Passive Buzzer

PWM Vibration Motor Sensor Module Switch

128x64 OLED Display (I2C IIC SPI Serial)

Documented Code

#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>

// Define OLED display size
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64

// OLED I2C Address and Reset Pin
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// Define pins
int LEDRed = 9;       // Red LED (Very Dangerous Level)
int LEDYellow = 10;   // Yellow LED (Dangerous Level)
int LEDGreen = 11;    // Green LED (Normal Level)
int buzzer = 8;       // Buzzer
int motor = 7;        // Vibration Motor
int smokeA0 = A0;     // MQ-7 output connected to A0

// Calibration constants
float V_ref = 5.0;    // Reference voltage
float RL = 10.0;      // Load resistance in kOhms
float R0 = 10.0;      // Sensor baseline resistance in clean air

// Thresholds
float normalThreshold = 100.0;
float dangerousThreshold = 150.0;
float veryDangerousThreshold = 200.0;

// Variables
int analogSensor;     // Analog sensor reading
float VRL, Rs, ppm;   // Voltage across load, sensor resistance, and ppm value

void setup() {
  // Initialize pins
  pinMode(LEDRed, OUTPUT);
  pinMode(LEDYellow, OUTPUT);
  pinMode(LEDGreen, OUTPUT);
  pinMode(buzzer, OUTPUT);
  pinMode(motor, OUTPUT);
  pinMode(smokeA0, INPUT);

  // Start Serial Monitor
  Serial.begin(9600);

  // Initialize OLED
  if (!display.begin(0x3C, OLED_RESET)) {  // I2C address set to 0x3C
    Serial.println(F("OLED initialization failed! Check connections."));
    for (;;);  // Halt execution if OLED fails
  }
  
  // Clear and prepare OLED
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0, 0);
  display.println("Initializing...");
  display.display();
  delay(2000);  // Show initializing message
}

void loop() {
  // Read sensor data
  analogSensor = analogRead(smokeA0);
  VRL = (analogSensor / 1023.0) * V_ref;       // Convert analog value to voltage

  // Check for invalid sensor readings
  if (analogSensor < 10) { // Threshold for detecting disconnected or invalid sensor
    ppm = 0; // Set ppm to 0 for invalid readings
  } else {
    Rs = RL * ((V_ref - VRL) / VRL);            // Calculate sensor resistance
    ppm = calculatePPM(Rs / R0);                // Calculate ppm from Rs/R0 ratio
  }

  // Clear OLED and display data
  display.clearDisplay();
  display.setCursor(0, 0);
  display.print("PPM: ");
  display.println(ppm);

  // LED, Buzzer, and Motor Control Based on Thresholds
  if (ppm >= veryDangerousThreshold) {
    display.println("Level: VERY DANGEROUS");
    activateAlert(LEDRed, 2000, motor, true); // Red LED, continuous buzzer, motor on
  } else if (ppm > normalThreshold && ppm <= dangerousThreshold) {
    display.println("Level: DANGEROUS");
    activateAlert(LEDYellow, 1500, motor, false); // Yellow LED, intermittent buzzer, motor on/off
  } else if (ppm <= normalThreshold && ppm > 0) {
    display.println("Level: NORMAL");
    activateNormal(LEDGreen); // Green LED, no buzzer, motor off
  } else {
    // No valid readings
    display.println("No Sensor Data");
    activateNormal(-1); // Turn off all outputs
  }

  // Display updates
  display.display();

  // Serial output for debugging
  Serial.print("Analog Value: ");
  Serial.print(analogSensor);
  Serial.print(" | PPM: ");
  Serial.println(ppm);

  delay(500); // Adjust reading interval
}

// Function to calculate ppm
float calculatePPM(float ratio) {
  return pow(10, (log10(ratio) - log10(1.0)) * 0.3 + 2.0); // Replace constants with calibration values if needed
}

// Function to handle dangerous and very dangerous alerts
void activateAlert(int ledPin, int buzzerFrequency, int motorPin, bool continuous) {
  digitalWrite(LEDRed, LOW);
  digitalWrite(LEDYellow, LOW);
  digitalWrite(LEDGreen, LOW);
  if (ledPin != -1) digitalWrite(ledPin, HIGH);        // Activate corresponding LED
  tone(buzzer, buzzerFrequency);                      // Activate buzzer
  if (continuous) {
    digitalWrite(motorPin, HIGH);                     // Keep motor on continuously
  } else {
    digitalWrite(motorPin, HIGH);
    delay(500);                                       // Intermittent motor activation
    digitalWrite(motorPin, LOW);
  }
}

// Function to handle normal or invalid level
void activateNormal(int ledPin)