If you are building a flame detector Arduino project, the direct answer for a basic prototyping sensor is the KY-026 IR Flame Sensor module. It detects infrared light in the 760nm to 1100nm wavelength emitted by fire. However, because sunlight and incandescent bulbs also blast this exact IR spectrum, the KY-026 will false-trigger outdoors or near 60W lamps. For a reliable indoor node, you must pair it with software filtering and hysteresis. For life-safety or outdoor deployment, you must upgrade to a UV-specific sensor like the GUVA-S12SD.

Safety Caveat: DIY microcontroller flame detectors are for hobby monitoring, data logging, and learning. Under NFPA 72 (National Fire Alarm and Signaling Code), life-safety fire alarms require listed, supervised equipment. Never replace a certified smoke/heat alarm with an Arduino for life-safety compliance.

The Verdict: Which Flame Sensor Module to Buy

Not all flame sensors are created equal. The right choice depends entirely on your deployment environment and the heat source you are tracking. Use this decision path to select your hardware.

Use CaseSensor TypeSpecific ModuleWavelengthEst. Cost
School project, candle/lighter detection indoorsIR PhototransistorKY-026 (with LM393)760nm - 1100nm$2.00
Room fire detection, immune to sunlight/incandescentUV PhotodiodeGUVA-S12SD Module240nm - 320nm$12.00
Industrial hotspot mapping, smoldering firesThermal ArrayMLX90640 (I2C)Far-IR (Heat)$45.00

Default Pick for this Guide: We are using the KY-026. It is the most common, cheapest, and easiest to wire for beginners. If your project requires ignoring ambient sunlight, terminate this path and buy the GUVA-S12SD instead.

Parts List & Spec Sheet

This build targets the Arduino Nano V3 (ATmega328P, USB-C variant). The Nano is chosen over the Uno for its compact breadboard footprint, making it ideal for taping inside a project enclosure. The CH340G USB-C variant is recommended over the older Mini-USB FTDI boards for modern cable compatibility and reliable driver support on Windows 11 and macOS.

  • Microcontroller: Arduino Nano V3 (ATmega328P, CH340G USB-C) — $6.00
  • Sensor: KY-026 Flame Sensor Module (includes LM393 comparator & potentiometer) — $2.00
  • Alert: 5V Active Buzzer (KY-012 or generic 5V piezo with internal oscillator) — $1.50
  • Power/Logic: 10kΩ resistor (for optional digital pin pull-up if bypassing LM393) — $0.10
  • Wiring: 22 AWG solid core jumper wires, half-size solderless breadboard — $5.00
Difficulty Rating: 2/5 (Beginner). Time to Build: 30 minutes. Arduino Nano official documentation confirms the ATmega328P ADC operates at 10-bit resolution (0-1023), which is perfectly matched to the KY-026 analog output.

Pin Mapping & Wiring Steps

The KY-026 has four pins: GND, VCC, DO (Digital Output), and AO (Analog Output). We will use the Analog Output to read the actual intensity of the flame, allowing our code to filter out noise. The Digital Output is tied to the onboard LM393 comparator and is less useful for software filtering.

KY-026 PinArduino Nano PinWire ColorNotes
VCC5VRedDo not use 3.3V; the LM393 needs 5V for full swing.
GNDGNDBlackEnsure common ground with the buzzer.
AOA0BlueAnalog input for raw IR intensity reading.
DOD2GreenOptional: Used for hardware interrupt fallback.

Buzzer Wiring: Connect the Buzzer VCC (Red) to Nano Pin D8, and Buzzer GND (Black) to Nano GND. Ensure you are using an active buzzer (which has an internal oscillator). If you use a passive buzzer, it will only click faintly unless you write a PWM tone-generation loop.

Compilable Arduino Code with Hysteresis

Cheap IR sensors suffer from 120Hz ripple caused by the flicker of AC-powered indoor lighting. If you just use a simple if (val < threshold) statement, your buzzer will chatter rapidly. The code below implements an Exponential Moving Average (EMA) low-pass filter to smooth out light flicker, and a hysteresis loop to prevent the alarm from toggling on and off when hovering near the threshold edge.

// Flame Detector Arduino Project - Arduino Nano V3 (ATmega328P)
// Target Board: Arduino Nano (Old Bootloader or standard depending on CH340 clone)

#define SENSOR_PIN A0
#define BUZZER_PIN 8
#define DIGITAL_PIN 2 // Fallback hardware interrupt pin

// Thresholds (0-1023). Lower value = MORE IR light (closer flame).
// Adjust these based your serial monitor readings with a lighter.
const int TRIGGER_THRESHOLD = 450; 
const int RESET_THRESHOLD = 650;   // Hysteresis gap to prevent chatter

// EMA Filter parameters
float filteredValue = 1023.0; 
const float ALPHA = 0.15; // Lower = smoother but slower response

bool alarmState = false;
int stuckHighCounter = 0;

void setup() {
  Serial.begin(115200);
  pinMode(BUZZER_PIN, OUTPUT);
  pinMode(DIGITAL_PIN, INPUT);
  digitalWrite(BUZZER_PIN, LOW);
  
  // Prime the filter with 50 rapid reads
  for(int i=0; i<50; i++) {
    filteredValue = (ALPHA * analogRead(SENSOR_PIN)) + ((1 - ALPHA) * filteredValue);
    delay(2);
  }
  Serial.println("[SYS] Flame detector initialized. EMA filter primed.");
}

void loop() {
  int rawValue = analogRead(SENSOR_PIN);
  
  // Error Handling: Check for disconnected sensor (pull-ups drag pin to 1023)
  if (rawValue >= 1020) {
    stuckHighCounter++;
    if (stuckHighCounter > 50) {
      Serial.println("[ERR] A0 Stuck HIGH (1023). Check VCC wire or sensor phototransistor.");
      delay(1000); // Throttle error messages
    }
  } else {
    stuckHighCounter = 0;
  }

  // Apply Exponential Moving Average (EMA) to kill 120Hz AC light ripple
  filteredValue = (ALPHA * rawValue) + ((1 - ALPHA) * filteredValue);

  // Hysteresis Logic
  if (!alarmState && filteredValue < TRIGGER_THRESHOLD) {
    alarmState = true;
    digitalWrite(BUZZER_PIN, HIGH);
    Serial.print("[ALARM] Flame detected! Filtered Val: ");
    Serial.println(filteredValue);
  } 
  else if (alarmState && filteredValue > RESET_THRESHOLD) {
    alarmState = false;
    digitalWrite(BUZZER_PIN, LOW);
    Serial.println("[SYS] Flame cleared. Alarm reset.");
  }

  // Debug output (throttled)
  static unsigned long lastPrint = 0;
  if (millis() - lastPrint > 250) {
    Serial.print("Raw: "); Serial.print(rawValue);
    Serial.print(" | Filtered: "); Serial.println(filteredValue);
    lastPrint = millis();
  }

  delay(10); // 10ms loop delay for stable ADC sampling
}

Debugging: First Three Things to Check When It Fails

When your serial monitor misbehaves or the hardware triggers falsely, follow this ranked decision path. These are the most common failure modes for IR flame sensors on the bench.

1. Symptom: Serial prints [ERR] A0 Stuck HIGH (1023)

This exact error string means the microcontroller's ADC is reading maximum voltage continuously. The flame sensor's phototransistor acts as a variable resistor to ground; if it's missing, the internal pull-up or floating noise maxes out the reading.

  • Cause A (Most Likely): The red VCC wire to the KY-026 is loose or disconnected. The sensor isn't powering the LM393 or the phototransistor bias.
  • Cause B: The analog pin A0 is accidentally shorted to the 5V rail on your breadboard.
  • Cause C: The IR phototransistor was destroyed by wiring VCC and GND backward, frying the silicon junction.

2. Symptom: False Triggers in Daylight or Near Desk Lamps

The alarm sounds when there is no fire, but the sun is shining through the window or a 60W incandescent bulb is turned on.

  • Cause A: The 760-1100nm IR spectrum is heavily emitted by the sun and hot tungsten filaments. Fix: Add a physical bandpass filter (a piece of dark red acrylic) over the sensor, or lower the TRIGGER_THRESHOLD to 200 so only intense, close-proximity IR triggers it.
  • Cause B: The EMA filter ALPHA value is too high (e.g., 0.8), allowing 120Hz light flicker to spike the filtered value momentarily. Fix: Drop ALPHA to 0.05.

3. Symptom: Buzzer is Silent or Just Clicking Faintly

  • Cause A: You bought a passive buzzer instead of an active buzzer. Passive buzzers require an AC square wave (using the tone() function) to make sound. Active buzzers only need DC HIGH. Fix: Swap the hardware, or replace digitalWrite(BUZZER_PIN, HIGH) with tone(BUZZER_PIN, 2000).
  • Cause B: The Nano's 5V rail is sagging under the buzzer's current draw (some cheap piezos draw 30mA+). Power the buzzer via a 2N2222 transistor if the Nano resets during alarms.

Extending or Simplifying the Build

Depending on your end goal, you can strip this project down to its bare minimum or scale it up into a supervised IoT node.

How to Simplify (The Bare-Metal Approach)

If you don't care about light flicker or exact intensity and just want a binary 'fire/no-fire' toggle, ditch the analog pin entirely. Wire only the DO (Digital Output) pin from the KY-026 to Nano Pin D2. Use a small flathead screwdriver to turn the blue trimpot on the KY-026 module until the onboard LED turns off, then back it off slightly. When a flame appears, the LM393 comparator pulls D2 LOW. You can replace the entire code loop with a simple attachInterrupt() routine, saving memory and processing overhead.

How to Extend (The IoT Supervised Node)

To make this a practical home-monitoring tool, upgrade the microcontroller to an ESP32-WROOM-32 DevKit V1. The ESP32's 12-bit ADC (0-4095) provides much finer resolution for flame distance estimation than the Nano's 10-bit ADC. Add a MQTT publishing block to the code to push the filteredValue to a local Home Assistant broker every 5 seconds. More importantly, implement a supervised heartbeat. In life-safety systems, a silent failure (like a dead sensor) is as dangerous as a fire. Program the ESP32 to send an MQTT 'Online' payload every 60 seconds. If Home Assistant misses two consecutive heartbeats, it triggers a 'Sensor Fault' automation, alerting you that the hardware has gone offline before a fire actually starts.