If you need to measure ambient light with a microcontroller, the best default photoresistor for Arduino is the GL5528 paired with a 10kΩ pull-down resistor. This specific combination provides the most linear voltage response across standard indoor room lighting (10 to 100 lux) without requiring complex calibration or external amplification.

While phototransistors and digital I2C sensors exist, the passive Light Dependent Resistor (LDR) remains the most cost-effective and forgiving component for hobbyist light triggers, automatic night lights, and plant grow-box monitors. Below is the exact decision framework, wiring physics, and production-ready code to get it running on the bench today.

Decision Tree: Which Light Sensor Should You Actually Buy?

Not all light sensors behave the same way. Before ordering parts, run your project requirements through this decision matrix to ensure you are not fighting the physics of the wrong component.

Sensor Type / Model Response Time Dark Resistance Best Use Case Drawbacks
GL5528 (LDR) Slow (~20ms) ~1 MΩ Room lighting, streetlamp triggers, indoor automation Non-linear, slow to react to strobes/lasers
GL5516 (LDR) Slow (~20ms) ~0.5 MΩ Bright outdoor sunlight tracking Too insensitive for dim indoor environments
TEMT6000 (Phototransistor) Fast (~15µs) N/A (Current output) Fast pulse counting, IR proximity, laser tripwires Requires transimpedance amplifier for high precision
TSL2561 (Digital I2C) Digital (Variable) N/A (Digital IC) Accurate lux logging, horticulture, UI auto-brightness Expensive (~$5), requires library and I2C wiring
The Verdict: If you are building a standard room-awareness project, night light, or solar tracker prototype, buy the GL5528. It costs pennies, requires only one passive resistor to interface with an ADC, and its spectral response peaks at 540nm (green), which closely mimics human eye sensitivity to ambient room light.

Parts List & Pin Mapping

This build targets the Arduino Uno R3 (ATmega328P, 5V logic, 10-bit ADC). If you are using a 3.3V board like the ESP32 or Arduino Nano 33 IoT, see the voltage warning below.

Required Components

  • Microcontroller: Arduino Uno R3 (or Nano v3 with ATmega328P)
  • Sensor: GL5528 Photoresistor (5mm package)
  • Pull-down Resistor: 10kΩ, 1/4W carbon film (±5% tolerance)
  • Indicator LED: 5mm standard diffused LED (any color)
  • Current Limiting Resistor: 220Ω or 330Ω for the LED
  • Wiring: 22 AWG solid core jumper wires
3.3V Board Safety Warning: The Arduino Uno outputs a 0-5V analog signal. If you wire this exact 5V voltage divider into a 3.3V ADC pin (like on an ESP32 or Raspberry Pi Pico), you will permanently damage the microcontroller's silicon. For 3.3V boards, change the pull-down resistor to 4.7kΩ and power the top of the divider from the board's 3.3V pin, not the 5V pin.

Pin Mapping Table

Component Component Pin / Leg Arduino Uno R3 Pin Notes
GL5528 LDR Leg 1 5V Polarity does not matter on LDRs
GL5528 LDR Leg 2 A0 (Analog In) Forms the voltage divider junction
10kΩ Resistor Leg 1 A0 (Analog In) Shares the same breadboard node as LDR Leg 2
10kΩ Resistor Leg 2 GND Pulls the analog pin to 0V in darkness
Indicator LED Anode (Long Leg) Pin 8 (via 220Ω) Digital output for threshold trigger
Indicator LED Cathode (Short Leg) GND Common ground with the resistor network

The Voltage Divider Physics (Why 10kΩ?)

A microcontroller cannot read resistance directly; it reads voltage. We must convert the GL5528's changing resistance into a 0V–5V signal using a voltage divider. The formula governing this junction is:

V_out = V_in × (R_pull-down / (R_LDR + R_pull-down))

The GL5528 has a dark resistance of roughly 1 MΩ and a bright light (10 lux) resistance of roughly 10kΩ to 20kΩ. If you use a 1kΩ pull-down resistor, your maximum voltage in bright light will only reach ~0.45V, wasting 90% of the Arduino's 10-bit ADC resolution (0-1023). If you use a 100kΩ pull-down, the voltage in normal room light will peg at 5V, saturating the sensor.

By choosing a 10kΩ pull-down, the junction voltage sits at roughly 2.5V (ADC value ~512) when the LDR is exposed to standard indoor office lighting (~20kΩ). This centers your measurement range, giving you maximum resolution for both shadows and bright flashes. For a deeper look at analog input scaling, refer to the official Arduino AnalogInOutSerial documentation.

Complete Arduino Code with Error Handling

High-impedance analog nodes (like an LDR in a dark room hitting 1 MΩ) act as antennas, picking up 50/60Hz electromagnetic interference from nearby mains wiring. A single analogRead() will fluctuate wildly. The code below implements a 16-sample oversampling array to average out AC ripple, alongside runtime fault detection for disconnected components.

/*
 * GL5528 Photoresistor Light Logger & Threshold Trigger
 * Target Board: Arduino Uno R3 (ATmega328P, 5V Logic)
 * Author: ElectricalFlux Bench Team
 */

// --- PIN DEFINITIONS ---
#define LDR_PIN       A0   // Analog input from voltage divider
#define LED_PIN       8    // Digital output for night-light trigger

// --- CALIBRATION & THRESHOLDS ---
#define DARK_THRESHOLD  300  // ADC value (0-1023) to trigger LED (approx dim room)
#define SAMPLE_SIZE     16   // Number of reads to average (filters 60Hz noise)
#define FAULT_LOW       5    // ADC value indicating a short to GND
#define FAULT_HIGH      1018 // ADC value indicating a floating/disconnected pin

// --- GLOBAL VARIABLES ---
unsigned long lastPrintTime = 0;
const unsigned long printInterval = 1000; // Print to serial every 1 second

void setup() {
  Serial.begin(115200);
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);
  
  // Allow ADC to stabilize after power-on
  delay(100);
  analogRead(LDR_PIN); // Dummy read to clear multiplexer charge
  
  Serial.println(F("System Initialized. Monitoring GL5528 on A0..."));
}

void loop() {
  int rawLightLevel = readFilteredLDR();
  
  // --- RUNTIME ERROR HANDLING ---
  if (rawLightLevel <= FAULT_LOW) {
    Serial.println(F("ERROR: Sensor shorted to GND or pull-up missing (Reading: 0)"));
    digitalWrite(LED_PIN, LOW);
    delay(500);
    return;
  }
  
  if (rawLightLevel >= FAULT_HIGH) {
    Serial.println(F("ERROR: Sensor floating or pull-down missing (Reading: 1023)"));
    digitalWrite(LED_PIN, HIGH); // Blink LED as a visual hardware fault alarm
    delay(100);
    digitalWrite(LED_PIN, LOW);
    delay(100);
    return;
  }

  // --- NORMAL OPERATION ---
  // Map ADC value to an approximate Lux scale (highly non-linear, for relative logging only)
  // Formula derived from typical GL5528 datasheet curve: Lux ≈ 32000 * (V_out / (5 - V_out))^(-1.4)
  // For simplicity in integer math, we use a mapped approximation.
  int approxLux = map(rawLightLevel, 0, 1023, 0, 1000);
  
  // Threshold Trigger
  if (rawLightLevel < DARK_THRESHOLD) {
    digitalWrite(LED_PIN, HIGH); // Turn on night light
  } else {
    digitalWrite(LED_PIN, LOW);
  }

  // Throttled Serial Logging
  if (millis() - lastPrintTime >= printInterval) {
    lastPrintTime = millis();
    Serial.print(F("ADC Raw: "));
    Serial.print(rawLightLevel);
    Serial.print(F(" | Approx Lux: "));
    Serial.println(approxLux);
  }
}

// --- FILTERING FUNCTION ---
// Averages 16 rapid samples to eliminate 50/60Hz mains hum on high-impedance nodes
int readFilteredLDR() {
  long sum = 0;
  for (int i = 0; i < SAMPLE_SIZE; i++) {
    sum += analogRead(LDR_PIN);
    delay(2); // 2ms delay spaces samples across the AC waveform
  }
  return (int)(sum / SAMPLE_SIZE);
}

Debugging: Why is My analogRead() Stuck at 0 or 1023?

When prototyping analog sensors, the most common failure mode is a hard-pegged reading. If your Serial Monitor outputs the exact error strings coded above, follow this diagnostic path.

First 3 Things to Check When It Fails

  1. Breadboard Power Rail Continuity: Many cheap breadboards have split power rails in the middle. Use a multimeter in continuity mode to verify that the 5V and GND rails actually reach the rows where your LDR and 10kΩ resistor are plugged in.
  2. Resistor Value Verification: Pull the 10kΩ resistor out of the board and measure it with a DMM. Color bands can be misread (e.g., confusing a 100kΩ for a 10kΩ). It should read between 9.5kΩ and 10.5kΩ.
  3. Breadboard Node Shorting: LDR legs are thick and can bend the internal breadboard contacts, causing the A0 node to short directly to the adjacent GND or 5V rail. Inspect the junction with a magnifying glass.

Ranked Causes for Specific Error Strings

Serial Monitor Error String Most Likely Cause (Ranked) How to Fix
ERROR: Sensor shorted to GND... 1. 10kΩ pull-down resistor is missing or installed in the wrong row.
2. LDR leg is bent and touching the GND rail.
3. A0 pin is internally damaged.
Verify the physical path from A0 to GND. Ensure the 10kΩ resistor bridges the A0 row and the GND row.
ERROR: Sensor floating... 1. 5V wire to the top of the LDR is disconnected.
2. The LDR itself is internally cracked (infinite resistance).
3. Pull-down resistor is missing.
Measure voltage across the LDR legs with a DMM. If it reads 0V, your 5V supply isn't reaching the component.
Compiler Error: 'A0' was not declared in this scope 1. Code placed outside of setup() or loop().
2. Missing #define LDR_PIN A0 at the top of the sketch.
Ensure all pin definitions are in the global scope above void setup().

For more advanced troubleshooting on analog noise and ADC grounding issues, the Adafruit Photocell Tutorial provides excellent visual references for breadboard layouts.

Extending and Simplifying the Build

Once you have the baseline GL5528 circuit working, you will eventually hit the limits of analog resistance-based sensing. Here is how to scale the project up or down based on your final application.

How to Simplify (For Production / PCB Design)

If you are moving this from a breadboard to a custom PCB for a commercial night-light product, drop the GL5528 and the 10kΩ resistor. Instead, use a PTN3315 or similar integrated ambient light sensor IC. It eliminates the need for manual voltage divider tuning, draws microamps of quiescent current, and provides a clean digital I2C output, reducing your BOM (Bill of Materials) complexity and calibration time on the assembly line.

How to Extend (For Smart Home Integration)

To turn this into an IoT light logger:

  1. Swap the Board: Replace the Uno R3 with an ESP32-WROOM-32 DevKit v1.
  2. Fix the Voltage: Remember the 3.3V warning. Power the divider from the ESP32's 3.3V pin and use a 4.7kΩ pull-down resistor.
  3. Add Connectivity: Use the WiFi.h and PubSubClient libraries to publish the approxLux integer to an MQTT broker (like Mosquitto or Home Assistant) every 5 seconds.
  4. Add Hysteresis: To prevent the LED from flickering rapidly when a cloud passes over the sensor, implement a software deadband. Only turn the LED off if the reading exceeds DARK_THRESHOLD + 50.
Final Recommendation: Do not overcomplicate your first light-sensing build. Start with the GL5528 and a 10kΩ resistor on an Arduino Uno R3. Master the voltage divider physics and the oversampling code provided above before spending $15 on a digital I2C lux sensor. The analog fundamentals you learn here apply to every other resistive sensor you will ever wire, from thermistors to flex sensors.