A light sensor works by converting photons into electrical resistance or current via the photoelectric effect. For modern ESP32 and Arduino projects, skip the analog LDR and use the BH1750 (GY-302 module). It outputs a calibrated digital I2C lux value (raw data / 1.2), operates from 3.0V to 5.0V, and completely bypasses the ESP32’s notoriously non-linear internal ADC.

The Physics: How Does the Light Sensor Work?

At the semiconductor level, light sensing relies on the photoelectric effect and photoconductivity. When photons strike a semiconductor material—such as cadmium sulfide in a photoresistor or silicon in a photodiode—they transfer energy to electrons. If the photon energy exceeds the material's bandgap, electrons are excited into the conduction band. In a photoresistor (LDR), this electron excitation drops the electrical resistance. In a photodiode, the excited electrons generate a proportional photocurrent that can be measured by an amplifier.

Digital ambient light sensors like the ROHM BH1750 or AMS TSL2591 take this a step further by packaging a photodiode array with an integrated analog-to-digital converter (ADC) and I2C logic. Crucially, they apply specialized optical coatings and dual-diode architectures to filter out infrared (IR) light. This mimics the human eye's photopic response curve, allowing the internal logic to output a direct, standardized lux value rather than a raw, uncalibrated resistance or current.

Analog vs. Digital: What the Output Actually Is

Understanding the exact nature of the output signal is where most hobbyist circuits fail. You must treat analog and digital sensors as fundamentally different interfaces.

  • Analog LDR (e.g., GL5528): The output is a variable resistance (typically 10kΩ to 20kΩ at 10 lux, dropping to 1kΩ at 100 lux). Microcontrollers cannot read resistance directly. You must build a voltage divider with a fixed resistor to convert this into an analog voltage (0V to 3.3V) for the MCU's ADC pin.
  • BH1750: The output is a digital 16-bit integer transmitted over I2C. The sensor's internal ADC handles the conversion, and the raw integer maps directly to lux with a simple division operation.
  • TSL2591: The output consists of two 16-bit digital channels over I2C: one for Visible + IR light, and one for IR only. You must use both channels in a mathematical formula to calculate the final lux value.
Hardware Warning: Never feed a 5V LDR voltage divider directly into an ESP32 GPIO. The ESP32 ADC pins are strictly 3.3V tolerant. Exceeding 3.3V will permanently damage the pin's internal ESD diodes and brick the ADC channel.

Wiring and Pinout Reference

Below is the standard wiring matrix for interfacing these three common light sensors with an ESP32 DevKit v1. Note the supply ranges; while the BH1750 and TSL2591 have onboard voltage regulators allowing 5V operation, the LDR divider must be tied to 3.3V.

Sensor Module Supply Range (VCC) GND Data / Signal Pin ESP32 Target Pin
GL5528 LDR (w/ 10kΩ Pull-down) 3.3V (Strict) GND Analog Out (Divider Midpoint) GPIO 34 (ADC1_CH6)
GY-302 (BH1750) 3.0V - 5.0V GND SDA / SCL GPIO 21 / GPIO 22
Adafruit 1980 (TSL2591) 3.3V - 5.0V GND SDA / SCL / INT GPIO 21 / GPIO 22 / GPIO 14

Raw-to-Lux Math and Calibration

Translating raw sensor readings into physical units (Lux) requires specific math depending on your hardware. A single lumen per square meter equals one lux.

1. The LDR Voltage Divider Math

For an LDR tied to 3.3V with a 10,000Ω fixed resistor to ground, the ESP32's 12-bit ADC returns a raw value between 0 and 4095. First, calculate the voltage:

V_out = (ADC_Raw / 4095.0) * 3.3

Next, calculate the LDR's resistance:

R_LDR = 10000 * (V_out / (3.3 - V_out))

Finally, apply the empirical log-log approximation for the GL5528 cadmium sulfide curve to estimate Lux:

Lux ≈ 500 * pow((R_LDR / 1000), -0.7)

Calibration Note: Factory tolerance on LDRs is ±50%. You must manually calibrate the '500' multiplier against a known lux meter if you need accuracy.

2. BH1750 Digital Math

The ROHM BH1750FVI datasheet specifies a resolution of 1 lux per count in high-resolution mode. However, the raw I2C payload must be scaled:

Lux = Raw_16bit_Value / 1.2

If the sensor returns a raw hex value of 0x5DC0 (24000 decimal), the ambient light is exactly 20,000 lux. No calibration is required; the IC is factory-trimmed.

3. TSL2591 High-Dynamic Range Math

The TSL2591 requires calculating a ratio between Channel 0 (Full Spectrum) and Channel 1 (Infrared). The Adafruit TSL2591 library handles this via the equation:

Lux = ((C0 - C1) * (1 - (C1 / C0))) / (Gain * Integration_Time)

Because the coefficients change based on the specific package window, always rely on the manufacturer's library for this specific IC rather than writing the I2C byte-shifting from scratch.

Interference Sources and Mitigation

Light sensors are highly susceptible to environmental noise. If your readings are erratic, check these three interference sources:

  1. 50/60Hz Mains Flicker: AC-powered LED and fluorescent lights pulse at 100Hz or 120Hz. An LDR's response time (20-50ms) is fast enough to capture this ripple, resulting in wild ADC swings. Fix: Add a 10µF electrolytic capacitor in parallel with the LDR's fixed resistor to create a hardware low-pass filter, or use the BH1750, which natively integrates light over 120ms to average out AC flicker.
  2. Infrared (IR) Contamination: Incandescent bulbs, halogen lamps, and direct sunlight emit massive amounts of IR radiation. A raw photodiode or LDR will read this IR as "visible" light, artificially inflating your lux readings. Fix: Use the BH1750 or TSL2591, which feature integrated IR-blocking optical filters.
  3. ESP32 ADC Non-Linearity: The ESP32's internal ADC is notoriously inaccurate at the extremes. It has a deadzone below ~0.1V (reads 0) and saturates around 3.1V (reads 4095). Fix: If you must use an LDR, design your voltage divider so the expected operating voltage sits squarely between 0.5V and 2.5V. Better yet, use an I2C digital sensor to bypass the ESP32 ADC entirely, as noted in the Espressif ADC documentation.

Decision Tree: Which Sensor to Pick

Use this decision matrix to select the correct component for your specific embedded application.

Application Requirement Recommended Sensor Why?
Budget < $1.00; simple day/night triggering for relays GL5528 LDR Cheapest option; absolute lux accuracy doesn't matter for basic thresholding.
Indoor smart lighting, plant growth tents, HVAC automation BH1750 (GY-302) Outputs true Lux over I2C; ignores 50/60Hz flicker; 1 to 65,535 lux range covers all indoor scenarios.
Outdoor weather stations; direct sunlight measurement TSL2591 High dynamic range handles up to 88,000 lux without saturating; excellent IR rejection for solar spectrum.
The Default Recommendation: For 90% of ESP32 smart home, automated blind, and indoor agriculture builds, buy the GY-302 BH1750 module (typically $1.50 to $2.50). It requires zero calibration math, natively outputs Lux over I2C, avoids the ESP32's ADC non-linearity, and its 120ms integration time naturally filters out annoying AC mains light flicker.