If you need to detect whether a room is dark or light, trigger a nightlight, or build a basic solar tracker, the photoresistor light sensor is the most cost-effective component in your bin. A bare GL5528 cadmium sulfide (CdS) cell costs roughly $0.15 in bulk, making it a staple for hobbyist embedded projects. However, because it is a purely passive resistive device, interfacing it correctly with a microcontroller's Analog-to-Digital Converter (ADC) requires a solid understanding of voltage dividers, non-linear scaling, and hardware noise filtering.

The Sensing Principle: How a Photoresistor Light Sensor Works

A photoresistor light sensor, commonly known as a Light Dependent Resistor (LDR), operates on the principle of photoconductivity. When photons strike the semiconductor material (typically cadmium sulfide in hobbyist parts like the GL5528), they excite electrons into the conduction band, drastically lowering the component's electrical resistance. In complete darkness, a standard GL5528 exhibits a resistance of roughly 1 MΩ to 3 MΩ, but under bright sunlight (10,000 lux), that resistance plummets to between 1 kΩ and 5 kΩ.

Because the component is purely passive, it does not output a voltage, current, or digital signal on its own. To interface it with a microcontroller, you must pair it with a fixed resistor to form a voltage divider. This converts the changing resistance into a variable analog voltage that the microcontroller's ADC can read. Unlike active digital sensors (e.g., the BH1750) that output calibrated I2C lux values, the photoresistor light sensor requires external scaling and calibration to translate voltage into meaningful physical units.

Hardware Wiring and Pinout Specifications

To read the sensor, we use a voltage divider circuit. The LDR is connected between the supply voltage (VCC) and the analog input pin, while a fixed 'pulldown' resistor is connected between the analog input pin and ground (GND). As light increases, the LDR's resistance drops, allowing more current to flow and raising the voltage at the analog pin.

According to standard voltage divider principles, the fixed resistor value should ideally match the LDR's resistance at the mid-point of your target lighting range. For general indoor/outdoor detection, a 10 kΩ pulldown resistor is the standard choice.

Table 1: Photoresistor Light Sensor Wiring and Specifications
Parameter / Pin Specification / Connection Notes & Constraints
Supply Range (VCC) 3.3V to 5.0V DC Match to your MCU logic level. The bare CdS cell can handle up to 150V, but MCU ADCs cannot.
LDR Leg 1 VCC (3.3V or 5V) Polarity does not matter; LDRs are non-polarized.
LDR Leg 2 MCU Analog Pin (e.g., A0, GPIO 34) Must be an ADC-capable pin. Do not use digital-only pins.
Fixed Resistor (10kΩ) Between Analog Pin and GND Acts as the pulldown. 1% tolerance metal film recommended for stability.
Output Signal Type Analog Voltage (0V to VCC) This is strictly an analog output. Do not conflate this with digital LDR modules.
Callout Tip: Bare LDR vs. Pre-built Sensor Modules
Many breakout boards sold online include an LM393 comparator chip alongside the LDR. These modules provide both an analog output (AO) and a digital output (DO). The DO pin simply fires HIGH or LOW based on a threshold set by a potentiometer. In this guide, we are strictly addressing the raw analog output. If you are using a module, ensure you wire the AO pin to your microcontroller's ADC, not the DO pin.

Reading the Output: Raw ADC to Lux Math

The raw output from your microcontroller is an integer representing the ADC count (0-1023 for a 10-bit Arduino Uno, or 0-4095 for a 12-bit ESP32). To make this useful, we must convert the raw count to voltage, then to resistance, and finally to an estimated Lux value.

According to the GL5528 datasheet, the relationship between resistance and illuminance is highly non-linear. It follows a log-log curve. The standard approximation formula for a CdS cell is:

1. ADC to Voltage:
V_out = (ADC_raw / ADC_max) * V_ref

2. Voltage to LDR Resistance:
R_ldr = R_fixed * ((V_ref / V_out) - 1.0)

3. Resistance to Lux (Approximation):
Because the curve is logarithmic, a simple linear multiplier won't work across all lighting conditions. A practical empirical formula for the GL5528 in the 10 to 1000 lux range is:
Lux ≈ 500 / R_kΩ (where R_kΩ is the LDR resistance in kilo-ohms).
For a broader range, use the gamma constant ($\gamma \approx 0.7$ for CdS):
Lux = 10 * (R_ldr / R_10)^(-1 / gamma)

Here is the complete, copy-pasteable C++ code for an ESP32 (using a 12-bit ADC and 3.3V reference) that implements this math with basic oversampling to reduce noise:

// ESP32 Photoresistor Light Sensor Code
const int LDR_PIN = 34;      // ADC1_CH6 (GPIO 34)
const float V_REF = 3.3;     // ESP32 3.3V reference
const int ADC_MAX = 4095;    // 12-bit resolution
const float R_FIXED = 10000; // 10k ohm pulldown resistor

void setup() {
  Serial.begin(115200);
  analogReadResolution(12);  // Set ESP32 ADC to 12-bit
  analogSetAttenuation(ADC_11db); // Full 3.3V range
}

void loop() {
  // 1. Oversample to reduce ADC noise (read 16 times and average)
  long adc_sum = 0;
  for(int i = 0; i < 16; i++) {
    adc_sum += analogRead(LDR_PIN);
    delayMicroseconds(100);
  }
  int adc_raw = adc_sum / 16;

  // Prevent division by zero in dark conditions
  if(adc_raw < 5) adc_raw = 5; 

  // 2. Convert Raw ADC to Voltage
  float v_out = (adc_raw * V_REF) / ADC_MAX;

  // 3. Convert Voltage to Resistance (Ohms)
  float r_ldr = R_FIXED * ((V_REF / v_out) - 1.0);
  float r_ldr_k = r_ldr / 1000.0; // Convert to kilo-ohms

  // 4. Convert Resistance to Estimated Lux
  // Empirical approximation for GL5528 mid-range
  float lux = 500.0 / r_ldr_k; 

  Serial.print("ADC: "); Serial.print(adc_raw);
  Serial.print(" | R_ldr: "); Serial.print(r_ldr); Serial.print(" ohms");
  Serial.print(" | Est. Lux: "); Serial.println(lux);

  delay(500);
}

Troubleshooting Interference and Signal Noise

If your serial monitor shows erratic Lux values jumping by hundreds of points while the sensor sits still on your desk, you are dealing with signal interference. The Espressif ESP32 ADC documentation explicitly notes that the ADC is susceptible to noise from internal Wi-Fi/Bluetooth radios and external electromagnetic interference. Here are the most common interference sources and how to fix them:

  • 50Hz/60Hz Mains Flicker: Fluorescent tubes and cheap LED drivers pulse at twice the mains frequency (100Hz or 120Hz). Because the LDR reacts in milliseconds, it will pick up this ripple. Fix: Increase your software oversampling window to 20ms to average out a full AC cycle, or add a hardware 100nF ceramic capacitor in parallel with the fixed pulldown resistor to create a low-pass RC filter.
  • ESP32 ADC2 Wi-Fi Conflict: The ESP32's ADC2 pins (GPIO 4, 12, 13, 14, 15, 25, 26, 27) are shared with the Wi-Fi driver. If Wi-Fi is active, ADC2 readings will fail or return garbage. Fix: Always use ADC1 pins (GPIO 32, 33, 34, 35, 36, 39) for analog sensors on the ESP32.
  • ADC Non-Linearity at Extremes: Microcontroller ADCs are notoriously inaccurate near 0V and VCC. If your LDR reads 4095 in bright light, the actual voltage might be slightly higher than the reference. Fix: Choose a pulldown resistor that keeps your expected operating voltage between 0.5V and 2.8V, avoiding the rails.
  • Thermal Drift: CdS cells exhibit slight resistance changes based on ambient temperature. If your sensor is mounted near a heat-generating component (like a voltage regulator or a power LED), your baseline dark resistance will drift. Fix: Keep the LDR thermally isolated from heat sinks.

Photoresistor Light Sensor FAQ

Can I use a photoresistor light sensor for precise lux measurement?

No. The spectral response of a cadmium sulfide photoresistor light sensor is heavily skewed toward green and yellow light (peaking around 540nm). It does not match the CIE human eye photopic V-lambda curve, meaning it will wildly misread blue-heavy LED light or infrared-heavy incandescent light. If your project requires accurate, calibrated lux measurements for horticulture or screen brightness matching, spend the extra $1.50 on a digital BH1750 or TSL2591 I2C sensor.

Why is my ESP32 giving erratic analog readings with the LDR?

The ESP32's internal ADC is noisier than the ATmega328P found on the Arduino Uno. Erratic readings are usually caused by reading from an ADC2 pin while Wi-Fi is enabled, or by lacking a bypass capacitor. Ensure you are using an ADC1 pin (like GPIO 34), solder or firmly seat a 100nF (0.1µF) ceramic capacitor across the analog input pin and GND, and use software oversampling (reading the pin 16 to 64 times and averaging the result) before calculating your final voltage.

What is the difference between a bare LDR and an LDR sensor module?

A bare LDR (like the GL5528) is just the two-legged resistive component. It requires you to build your own voltage divider on a breadboard or PCB. An LDR sensor module is a pre-built PCB that includes the LDR, a fixed voltage divider resistor, and usually an LM393 comparator chip. The module provides an Analog Output (AO) which acts exactly like the bare LDR divider, and a Digital Output (DO) which snaps HIGH or LOW based on a physical threshold adjusted by a blue trimpot on the board.