If you want to build a fire detection sensor Arduino project that actually works on the bench, you need to move past single-sensor tutorials. A robust hobbyist fire alarm requires two detection methods: an MQ-2 gas sensor to detect smoldering smoke and combustible gases, and an IR flame sensor to catch fast-moving open flames. This guide walks you through building a dual-sensor system targeting the Arduino Uno R3 (ATmega328P), complete with non-blocking alarm logic, serial telemetry, and hardware debugging routines.

Safety Caveat: This build is for educational and hobbyist monitoring purposes. It is not a substitute for a UL-listed, NFPA 72-compliant life-safety smoke detector. Never rely on a DIY microcontroller project as your primary life-safety alarm in a residential or commercial space.

Sensor Selection and Hardware Specifications

The most common mistake in DIY fire alarms is relying solely on a smoke sensor. Smoke sensors like the MQ-2 require a 24-hour burn-in period and can be slow to react to a sudden, clean-burning fire. Conversely, IR flame sensors only trigger when they have a direct line of sight to the flame's 760nm–1100nm infrared emission. By combining both, you cover smoldering electrical fires and rapid open flames.

Below is the specification sheet for the exact modules used in this build. These values assume a 5V logic level and a 25°C ambient environment.

Parameter MQ-2 Smoke/Gas Module KY-026 IR Flame Sensor 5V Active Buzzer Module
Operating Voltage 5.0V DC (Analog/Digital) 3.3V to 5.0V DC 5.0V DC
Detection Target Smoke, LPG, Butane, Methane 760nm - 1100nm IR Light N/A (Output Device)
Analog Output Range 0V to 5V (Higher = more gas) 0V to 5V (Lower = closer flame) N/A
Digital Output Logic LOW when gas > threshold LOW when flame detected HIGH to activate
Response Time ~10 seconds (after burn-in) < 1 millisecond < 5 milliseconds
Typical Cost (2026) $2.50 - $4.00 $1.50 - $2.50 $1.00 - $1.50

Notice the inverse relationship in the KY-026 analog output: as the flame gets closer, the internal photodiode conducts more, dropping the voltage at the LM393 comparator's analog out pin. We will use the digital out (DOUT) pin for immediate triggering and the analog out for distance telemetry in the code below.

Parts List and Pin Mapping

To finish this build without a second trip to the electronics bin, gather the following exact components. We are using an active buzzer rather than a passive one so we don't have to generate PWM tone frequencies in the code, keeping the main loop clean.

  • 1x Arduino Uno R3 (or compatible ATmega328P clone)
  • 1x MQ-2 Gas/Smoke Sensor Module (with LM393 comparator board)
  • 1x KY-026 IR Flame Sensor Module
  • 1x 5V Active Buzzer Module
  • 1x 5mm Red LED (Alarm indicator)
  • 1x 5mm Green LED (System OK indicator)
  • 2x 220Ω Resistors (for LEDs)
  • 1x Solderless Breadboard and male-to-male jumper wires

Pin Mapping Table

Component Module Pin Arduino Uno Pin Notes
MQ-2 Sensor VCC 5V Do NOT use 3.3V; heater needs 5V
MQ-2 Sensor AOUT A0 Analog smoke density reading
MQ-2 Sensor DOUT D3 Digital threshold trigger
KY-026 Flame VCC 5V 3.3V also acceptable
KY-026 Flame AOUT A1 Analog flame proximity
KY-026 Flame DOUT D2 Digital flame trigger (Active LOW)
Active Buzzer I/O / VCC D8 Digital HIGH to sound
Red LED Anode (+) D9 (via 220Ω) Alarm state indicator
Green LED Anode (+) D10 (via 220Ω) Normal state indicator

Wiring Steps and Hardware Assembly

  1. Power Rails: Connect the Arduino 5V pin to the positive rail on your breadboard, and GND to the negative rail. Ensure both MQ-2 and KY-026 modules share a common ground with the Uno.
  2. MQ-2 Wiring: Connect the MQ-2 VCC to 5V. Route the AOUT pin to Arduino A0 and DOUT to D3. Pro-tip: The MQ-2 gets physically hot during operation. This is normal; the internal heating element must reach ~200°C to catalyze the gas reaction.
  3. KY-026 Wiring: Connect VCC to 5V, AOUT to A1, and DOUT to D2. Locate the blue trimpot (potentiometer) on the KY-026 module. You will need to tune this later with a lighter to set the digital trip point.
  4. Output Devices: Connect the active buzzer's positive pin to D8 and GND to the ground rail. For the LEDs, place a 220Ω resistor in series with the anode of each LED, connecting the resistors to D9 (Red) and D10 (Green), and the LED cathodes to ground.
  5. Pre-Heat Phase: Upload the code (below), then leave the Arduino powered on for 24 hours. The MQ-2 requires this initial burn-in to stabilize its baseline resistance. Do not attempt to calibrate your smoke thresholds until this period is complete.

Complete Arduino Code with Error Handling

This sketch targets the Arduino Uno R3. It uses non-blocking millis() timing to blink the alarm LED and beep the buzzer without halting the sensor polling loop. It also includes serial error handling to catch floating pins or sensor failures.

/*
 * Dual Fire Detection Sensor Arduino Project
 * Target Board: Arduino Uno R3 (ATmega328P)
 * Sensors: MQ-2 (Smoke), KY-026 (IR Flame)
 */

// --- Pin Definitions ---
const int PIN_MQ2_AOUT = A0;
const int PIN_MQ2_DOUT = 3;
const int PIN_FLAME_AOUT = A1;
const int PIN_FLAME_DOUT = 2;
const int PIN_BUZZER = 8;
const int PIN_LED_RED = 9;
const int PIN_LED_GREEN = 10;

// --- Thresholds & Timing ---
const int SMOKE_ANALOG_THRESHOLD = 350; // Adjust after 24h burn-in
const int FLAME_ANALOG_THRESHOLD = 600; // Lower voltage = closer flame
const unsigned long TELEMETRY_INTERVAL = 1000; // Serial print every 1s
const unsigned long ALARM_BLINK_INTERVAL = 250; // Buzzer/LED toggle rate

// --- State Variables ---
bool alarmState = false;
bool buzzerToneState = false;
unsigned long lastTelemetryTime = 0;
unsigned long lastBlinkTime = 0;

void setup() {
  Serial.begin(115200);
  
  // Initialize Pins
  pinMode(PIN_MQ2_DOUT, INPUT);
  pinMode(PIN_FLAME_DOUT, INPUT_PULLUP); // KY-026 DOUT is active LOW
  pinMode(PIN_BUZZER, OUTPUT);
  pinMode(PIN_LED_RED, OUTPUT);
  pinMode(PIN_LED_GREEN, OUTPUT);
  
  digitalWrite(PIN_BUZZER, LOW);
  digitalWrite(PIN_LED_RED, LOW);
  digitalWrite(PIN_LED_GREEN, HIGH); // System OK on startup
  
  Serial.println("System Initialized. Awaiting sensor data...");
  Serial.println("Note: Ensure MQ-2 has completed 24h burn-in for accurate baselines.");
}

void loop() {
  unsigned long currentMillis = millis();
  
  // 1. Read Sensors
  int smokeAnalog = analogRead(PIN_MQ2_AOUT);
  bool smokeDigital = digitalRead(PIN_MQ2_DOUT);
  int flameAnalog = analogRead(PIN_FLAME_AOUT);
  bool flameDigital = digitalRead(PIN_FLAME_DOUT);
  
  // 2. Hardware Fault Detection
  if (smokeAnalog >= 1020) {
    Serial.println("Error: MQ-2 analog read stuck at 1023. Check 5V power or sensor fault.");
  }
  if (flameAnalog <= 5) {
    Serial.println("Warning: Flame sensor analog near 0. Sensor may be saturated or shorted.");
  }
  
  // 3. Alarm Logic Evaluation
  // Trigger if smoke exceeds analog threshold OR flame digital pin goes LOW
  if (smokeAnalog > SMOKE_ANALOG_THRESHOLD || flameDigital == LOW) {
    alarmState = true;
  } else {
    alarmState = false;
  }
  
  // 4. Non-Blocking Alarm Actuation
  if (alarmState) {
    if (currentMillis - lastBlinkTime >= ALARM_BLINK_INTERVAL) {
      lastBlinkTime = currentMillis;
      buzzerToneState = !buzzerToneState;
      digitalWrite(PIN_BUZZER, buzzerToneState ? HIGH : LOW);
      digitalWrite(PIN_LED_RED, buzzerToneState ? HIGH : LOW);
      digitalWrite(PIN_LED_GREEN, LOW);
    }
  } else {
    // Reset to safe state
    digitalWrite(PIN_BUZZER, LOW);
    digitalWrite(PIN_LED_RED, LOW);
    digitalWrite(PIN_LED_GREEN, HIGH);
    buzzerToneState = false;
  }
  
  // 5. Non-Blocking Serial Telemetry
  if (currentMillis - lastTelemetryTime >= TELEMETRY_INTERVAL) {
    lastTelemetryTime = currentMillis;
    Serial.print("Smoke A0: "); Serial.print(smokeAnalog);
    Serial.print(" | Flame A1: "); Serial.print(flameAnalog);
    Serial.print(" | Flame D2: "); Serial.print(flameDigital ? "HIGH" : "LOW");
    Serial.print(" | Status: "); Serial.println(alarmState ? "ALARM" : "OK");
  }
}

Debugging: First Three Things to Check When It Fails

When your serial monitor outputs unexpected data or the hardware misbehaves, use this ranked decision path to isolate the fault. These are the exact failure modes encountered most frequently with these specific modules.

1. Exact Error: Error: MQ-2 analog read stuck at 1023

  • Cause A (Most Likely): The MQ-2 module is wired to the Arduino's 3.3V pin instead of 5V. The internal heater requires 5V to function. Without adequate heat, the tin dioxide (SnO2) sensing layer remains highly resistive, pulling the analog pin to VCC.
  • Cause B: The LM393 comparator trimpot on the back of the MQ-2 module is turned fully counter-clockwise, forcing the digital out low and skewing the analog circuit on cheap clone boards.
  • Fix: Move the VCC jumper to the 5V rail. Use a small Phillips screwdriver to adjust the blue trimpot while monitoring the serial output until the baseline rests around 150-250 in clean air.

2. Symptom: Flame Sensor Triggers Constantly (False Positives)

  • Cause A: The KY-026 is highly sensitive to ambient incandescent light and direct sunlight, both of which emit heavy IR in the 760-1100nm spectrum.
  • Cause B: The sensitivity potentiometer on the KY-026 is tuned too aggressively.
  • Fix: Shield the sensor from direct window light. Strike a lighter 12 inches away from the sensor, and slowly turn the trimpot clockwise until the digital DOUT pin flips from LOW to HIGH (LED on the module turns off). This sets your trip point precisely at 12 inches.

3. Exact Error: Warning: Flame sensor analog near 0

  • Cause: The photodiode is completely saturated by a massive IR source (like a halogen heat lamp or direct sun), or the analog out pin is physically shorted to ground on the breadboard.
  • Fix: Verify the physical wiring of the AOUT jumper. If wiring is correct, relocate the sensor away from heat lamps or incandescent bulbs. For advanced gas sensor tuning, ensure your environmental baseline isn't being skewed by external IR emitters.

Extending and Simplifying the Build

Depending on your end goal, you may want to strip this project down to its bare essentials or scale it up into a connected IoT node.

How to Simplify

If you are deploying this in a tight enclosure and only need a local hardware alarm, drop the serial telemetry entirely. Remove the Serial.print blocks from the loop to free up processing cycles, and desolder the LM393 comparator boards from the sensors. Wire the raw MQ-2 analog pin and the raw KY-026 photodiode directly to the Arduino ADC using a voltage divider, saving space and reducing the bill of materials by about $2.00.

How to Extend

To turn this into a smart-home integrated node, swap the Arduino Uno R3 for an ESP32-WROOM-32 DevKit v1. The ESP32 operates at 3.3V logic, which means you must either use logic level shifters for the MQ-2 digital out or rely solely on the analog outputs (keeping in mind the ESP32's ADC maxes out at ~3.1V). You can then use the PubSubClient library to push MQTT payloads to Home Assistant whenever the alarmState boolean flips to true.

For enhanced safety, add an MQ-7 Carbon Monoxide sensor. According to NFPA fire loss statistics, incomplete combustion and CO poisoning are leading hazards in residential fires. The MQ-7 requires a complex 5V/1.5V alternating heater cycle, which you can implement using a secondary transistor switching circuit driven by an ESP32 PWM pin, allowing you to detect the deadly, odorless gas that the MQ-2 will miss.