If you are building a fire-detection prototype, the KY-026 flame Arduino sensor is the default starting point. It detects infrared light emitted by flames (760nm to 1100nm) and outputs both an analog voltage and a digital trip signal. However, out of the box, these cheap modules are notorious for false positives from incandescent bulbs and stuck ADC readings due to reversed diode polarity.
This guide cuts through the generic tutorials. We will cover the exact hardware specs, provide a production-ready Arduino sketch with built-in error handling, and detail the bench-level debugging steps to fix the most common failure modes.
The KY-026 Flame Sensor: Specs and Operating Principles
The core of the KY-026 is an IR photodiode. Unlike standard clear LEDs, this diode is encapsulated in black epoxy. This isn't just for aesthetics; the black filter blocks visible light (400nm–700nm) while allowing infrared wavelengths to pass through to the silicon die. When IR photons hit the depletion region of the reverse-biased diode, they generate a tiny leakage current proportional to the light intensity.
This micro-current is too small for a microcontroller to read directly, so the module includes an LM393 dual comparator IC. The LM393 compares the voltage drop across the photodiode circuit against a reference voltage set by the onboard blue trimmer potentiometer.
KY-026 Module Specification Sheet
Here are the bench-measured and datasheet-derived specifications for the standard 4-pin KY-026 module. Keep these values in mind when designing your enclosure and power delivery.
| Parameter | Value / Rating | Notes & Bench Observations |
|---|---|---|
| Detection Wavelength | 760 nm – 1100 nm | Peak sensitivity around 940 nm. Matches standard wood/carbon fire emission. |
| Viewing Angle | ~60 Degrees | Highly directional. Fire outside this cone will yield a massive drop in analog sensitivity. |
| Operating Voltage (VCC) | 3.3V – 5.0V DC | LM393 operates down to 2V, but 5V is recommended for stable Arduino Uno ADC scaling. |
| Comparator IC | LM393 (Open-Collector) | Requires a pull-up resistor on the DO pin (usually 10kΩ, pre-soldered on the module). |
| Analog Output (AO) | 0V – VCC | Inversely proportional to flame intensity. High IR = Lower Voltage. |
| Digital Output (DO) | LOW (0V) / HIGH (VCC) | GOES LOW when flame IR exceeds the trim pot threshold. |
| Operating Temperature | -25°C to +85°C | The plastic housing will melt before the silicon fails; keep away from direct contact with fire. |
Hardware Setup: Parts List and Pin Mapping
For this build, we are targeting the Arduino Uno Rev3 (ATmega328P). The Uno's 10-bit ADC (0-1023) mapped to a 5V reference gives us a resolution of roughly 4.88mV per step, which is plenty for the KY-026's analog swing.
Required Components
- Microcontroller: Arduino Uno Rev3 (or compatible clone with ATmega328P and 5V logic).
- Sensor: KY-026 4-Pin Flame Sensor Module (Ensure it has both AO and DO pins; 3-pin variants lack the digital comparator output).
- Wiring: 4x Male-to-Female or Male-to-Male Dupont jumper wires.
- Prototyping: Half-size solderless breadboard (400 tie-points).
- Test Source: A standard butane lighter or a wooden match. (Do not use a heat gun; it emits IR but lacks the specific 940nm flame emission profile, which can confuse the sensor).
Pin Mapping Table
Wire the sensor to the Arduino exactly as shown below. Swapping the Analog and Digital pins is the #1 cause of 'stuck' readings in beginner builds.
| KY-026 Pin | Arduino Uno Rev3 Pin | Wire Color (Suggested) | Function |
|---|---|---|---|
| VCC | 5V | Red | Power supply for the LM393 and photodiode bias. |
| GND | GND | Black | Common ground reference. |
| DO | D2 | Yellow | Digital output (Active LOW when flame detected). |
| AO | A0 | Blue | Analog output (Continuous voltage proportional to IR). |
Complete Arduino Code with Error Handling
The following C++ sketch is designed for the Arduino Uno Rev3. It doesn't just read the pins; it includes a startup calibration routine to establish a baseline ambient IR level, a moving average filter to reject 60Hz AC flicker from room lighting, and explicit error handling for disconnected wires.
/*
* KY-026 Flame Sensor Robust Reader
* Target Board: Arduino Uno Rev3 (ATmega328P)
* Author: ElectricalFlux Bench Team
*/
// --- Pin Definitions ---
#define PIN_AO A0
#define PIN_DO 2
#define PIN_STATUS_LED 13
// --- Calibration & Filter Constants ---
#define BASELINE_SAMPLES 50 // Number of samples to average at startup
#define MOVING_AVG_SIZE 10 // Filter size to reject 50/60Hz light flicker
#define VARIANCE_THRESHOLD 50 // Minimum ADC swing expected during calibration
int movingAvgBuffer[MOVING_AVG_SIZE];
int bufferIndex = 0;
int baselineAmbient = 0;
void setup() {
Serial.begin(115200);
while (!Serial) { ; } // Wait for serial port (Leonardo/Micro, harmless on Uno)
pinMode(PIN_DO, INPUT); // LM393 DO is open-collector, relies on module pull-up
pinMode(PIN_STATUS_LED, OUTPUT);
Serial.println(F("KY-026 Flame Sensor Initialization..."));
// 1. Check for floating/disconnected Analog Pin
int rawTest = analogRead(PIN_AO);
if (rawTest >= 1020) {
Serial.println(F("ERROR: ADC_STUCK_HIGH_1023 - Check IR diode polarity and GND continuity."));
blinkErrorPattern();
}
// 2. Establish Ambient Baseline
long sum = 0;
for (int i = 0; i < BASELINE_SAMPLES; i++) {
sum += analogRead(PIN_AO);
delay(5);
}
baselineAmbient = sum / BASELINE_SAMPLES;
Serial.print(F("Baseline Ambient ADC: "));
Serial.println(baselineAmbient);
// Initialize moving average buffer
for (int i = 0; i < MOVING_AVG_SIZE; i++) {
movingAvgBuffer[i] = baselineAmbient;
}
Serial.println(F("System Ready. Monitoring for flame..."));
}
void loop() {
// Read and filter Analog
int rawADC = analogRead(PIN_AO);
movingAvgBuffer[bufferIndex] = rawADC;
bufferIndex = (bufferIndex + 1) % MOVING_AVG_SIZE;
long filteredSum = 0;
for (int i = 0; i < MOVING_AVG_SIZE; i++) {
filteredSum += movingAvgBuffer[i];
}
int filteredADC = filteredSum / MOVING_AVG_SIZE;
// Read Digital
bool flameDigital = (digitalRead(PIN_DO) == LOW); // Active LOW
// Calculate relative intensity (Higher number = more flame)
// Note: KY-026 AO voltage drops as IR increases, so ADC value drops.
int flameIntensity = baselineAmbient - filteredADC;
if (flameIntensity < 0) flameIntensity = 0;
// Status Output
if (flameDigital || flameIntensity > 200) {
digitalWrite(PIN_STATUS_LED, HIGH);
Serial.print(F("[ALERT] FLAME DETECTED | Intensity: "));
Serial.print(flameIntensity);
Serial.print(F(" | Raw ADC: "));
Serial.println(filteredADC);
} else {
digitalWrite(PIN_STATUS_LED, LOW);
// Throttle serial output to avoid flooding the buffer
static unsigned long lastPrint = 0;
if (millis() - lastPrint > 1000) {
Serial.print(F("[OK] Ambient | Intensity: "));
Serial.print(flameIntensity);
Serial.print(F(" | Raw ADC: "));
Serial.println(filteredADC);
lastPrint = millis();
}
}
delay(20); // ~50Hz loop rate
}
void blinkErrorPattern() {
while (true) {
// Fast double-blink to indicate hardware fault
for (int i = 0; i < 2; i++) {
digitalWrite(PIN_STATUS_LED, HIGH);
delay(100);
digitalWrite(PIN_STATUS_LED, LOW);
delay(100);
}
delay(600);
}
}
Debugging: Common Failures and the 'First Three Checks'
When a KY-026 fails to trigger, or the serial monitor throws the ERROR: ADC_STUCK_HIGH_1023 string, do not immediately assume the module is dead. The LM393 and the photodiode are robust; the issue is almost always in the physical layer or the calibration.
If your sensor is misbehaving, perform these first three checks in order:
- Verify IR Diode Polarity: The black component is a diode. It has a flat spot on the plastic rim indicating the cathode. If the module manufacturer soldered it backwards (common on sub-$1 clones), it will act as a reverse-biased block, and the analog pin will float high to 1023. Fix: Desolder and flip the diode, or use a multimeter in diode-test mode to verify forward voltage drop (~1.2V for IR diodes).
- Calibrate the LM393 Trim Pot: The blue potentiometer sets the reference voltage for the digital DO pin. If it is turned fully clockwise, the threshold might be higher than the sensor's maximum output, meaning the DO pin will never pull LOW. Fix: Strike a lighter 6 inches from the sensor. Use a small Phillips screwdriver to turn the pot until the onboard DO LED toggles on and off precisely at the edge of the flame's visibility.
- Check Power Rail Continuity (3.3V vs 5V): If you are powering the sensor from the Uno's 3.3V pin but reading the analog pin with the default 5V reference, your maximum ADC value will be capped around 675 instead of 1023. Fix: Wire VCC to the 5V pin on the Uno to match the Arduino analogRead() default reference.
Scaling the Build: Simplifications and Extensions
Depending on your end goal, you can strip this build down to its bare essentials or scale it up into a multi-node IoT network.
How to Simplify (The Digital-Only Alarm)
If you do not need to measure flame distance or intensity, drop the Analog (AO) pin entirely. Wire only VCC, GND, and DO. Calibrate the trim pot to your desired trip distance, and wire the DO pin directly to an active piezo buzzer (with a flyback diode) or a relay module. This removes the microcontroller from the loop entirely, creating a pure hardware fire trip circuit with zero latency and no code dependencies.
How to Extend (Dual-Spectrum Verification & IoT)
To eliminate false positives from sunlight and incandescent bulbs, professional systems use dual-spectrum detection. You can replicate this on the bench by adding a GUVA-S12SD UV sensor. Flames emit both IR and UV, whereas hot metal and lightbulbs emit IR but virtually zero UV. By requiring an AND logic condition in your code (IR > Threshold AND UV > Threshold), you achieve near-zero false alarm rates.
For remote monitoring, swap the Arduino Uno for an ESP32-WROOM-32 DevKit. The ESP32 operates at 3.3V, so you must power the KY-026 from the ESP32's 3.3V pin (the LM393 handles this fine, though your analog range will be 0-4095 on the ESP32's 12-bit ADC). You can then use the WiFi stack to publish the flame intensity to an MQTT broker, triggering Home Assistant automations or sending SMS alerts via Twilio when a fire is detected in a remote workshop.






