To wire a light sensor to an ESP32 or Arduino, you have two primary paths: connect a digital I2C sensor (like the BH1750) to the SDA/SCL pins with 3.3V power, or wire an analog LDR (photoresistor) in a voltage divider to an ADC pin. The digital route gives you factory-calibrated lux readings, while the analog route provides a cheap, relative voltage shift for simple day/night triggering.
The Sensing Principle: Photons to Electrons
Analog Light Dependent Resistors (LDRs), typically made of Cadmium Sulfide (CdS) like the popular GL5528, rely on internal photoelectric effects. When photons strike the semiconductor lattice, they excite electrons into the conduction band, drastically lowering the component's resistance from ~1MΩ in total darkness to ~1kΩ in bright sunlight. Because they are passive, they require an external circuit to convert this resistance change into a measurable voltage.
Digital ambient light sensors, such as the Rohm BH1750FVI, use a specialized photodiode integrated with a transimpedance amplifier and an onboard ADC. Instead of varying resistance, the IC measures the photocurrent, integrates it over a specific sampling window to reject 50/60Hz mains flicker, and outputs a calibrated digital value directly over an I2C bus. This makes the BH1750 immune to the voltage reference drift that plagues analog setups.
Wiring Pinout and Supply Requirements
Before stripping wires, you need to know exactly what your sensor expects. Supplying 5V to a 3.3V I2C module will permanently brick the onboard logic, while an LDR has no polarity or strict voltage limits (within reason).
| Feature | GL5528 LDR (Analog) | GY-302 BH1750 Module (Digital) |
|---|---|---|
| Supply Range | N/A (Passive Component) | 2.4V to 3.6V (3.3V nominal) |
| Output Type | Variable Resistance | Digital I2C (16-bit word) |
| Interface Pins | 2 (Non-polarized) | VCC, GND, SDA, SCL, ADDR |
| Typical Cost | ~$0.10 | ~$2.50 |
Step-by-Step Wiring and Output Signal Math
Here is how to physically wire both sensors to an ESP32 DevKit V1, followed by the exact math required to convert raw microcontroller readings into physical Lux units.
1. Wiring the Analog LDR (Voltage Divider)
- Connect one leg of the GL5528 LDR to the ESP32 3.3V pin.
- Connect the other leg of the LDR to GPIO 34 (an input-only ADC pin on the ESP32).
- Connect a 10kΩ pulldown resistor between GPIO 34 and GND.
The Math: The LDR and the 10kΩ resistor form a voltage divider. The voltage at GPIO 34 is V_out = 3.3V × (10kΩ / (R_LDR + 10kΩ)). The ESP32's 12-bit ADC maps 0-3.3V to raw values between 0 and 4095. To find the LDR resistance: R_LDR = 10000 × ((4095 / ADC_Raw) - 1). A rough empirical approximation for Lux is Lux ≈ 500 / (R_LDR in kΩ), though this requires physical calibration.
2. Wiring the Digital BH1750 (I2C)
- Connect the module VCC to the ESP32 3.3V pin.
- Connect GND to GND.
- Connect SCL to GPIO 22 (Default ESP32 I2C Clock).
- Connect SDA to GPIO 21 (Default ESP32 I2C Data).
- Leave ADDR floating (pulls low internally, setting I2C address to 0x23).
The Math: The BH1750 handles the analog-to-digital conversion internally. It returns a 16-bit raw payload (High Byte and Low Byte). The conversion to physical units is strictly defined in the Rohm datasheet: Lux = Raw_Value / 1.2.
Complete ESP32 Arduino Code
#include <Wire.h>
#include <BH1750.h>
// Pin Definitions
#define LDR_PIN 34
// Initialize BH1750 object
BH1750 lightMeter;
void setup() {
Serial.begin(115200);
Wire.begin(21, 22); // SDA, SCL for ESP32
// Initialize BH1750 with continuous high-res mode
if (lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE)) {
Serial.println("BH1750 Initialized.");
} else {
Serial.println("Error: BH1750 not found on I2C bus!");
}
}
void loop() {
// --- Analog LDR Reading ---
int adcRaw = analogRead(LDR_PIN);
// ESP32 ADC non-linearity correction (simplified for mid-range)
float voltage = (adcRaw / 4095.0) * 3.3;
float rLDR = 10000.0 * ((3.3 / voltage) - 1.0);
float ldrLux = 500.0 / (rLDR / 1000.0); // Rough approximation
// --- Digital BH1750 Reading ---
float bhLux = lightMeter.readLightLevel();
Serial.printf("LDR Raw: %d | LDR Lux: %.1f | BH1750 Lux: %.1f\n", adcRaw, ldrLux, bhLux);
delay(1000);
}
Calibration, Interference, and Real-World Gotchas
Calibration and Scaling: Analog LDRs are not precision instruments. A batch of GL5528 sensors can have a ±30% variance in dark resistance right out of the factory. If your project requires triggering lights at exactly 50 Lux, you must calibrate the LDR in code against a known commercial lux meter. The BH1750, conversely, is factory-trimmed to ±20% accuracy and requires zero software scaling beyond the / 1.2 math.
Common Interference Sources:
- Mains Flicker (50/60Hz): AC lighting pulses at 100/120Hz. A raw photodiode will read this as chaotic noise. The BH1750 solves this by integrating the light measurement over a 120ms window (in high-res mode), effectively averaging out the AC ripple. LDRs have a slow natural response time (~20ms) which naturally filters out high-frequency flicker, but they will lag in fast-moving shadow detection.
- Thermal Drift: CdS photoresistors are highly temperature-sensitive. As ambient heat rises, the dark resistance of an LDR drops, causing the microcontroller to falsely read that the room is getting brighter. Keep LDRs away from heat sinks and voltage regulators.
- I2C Bus Capacitance: If you run the BH1750 on wires longer than 30cm, bus capacitance will corrupt the I2C signal. Use 4.7kΩ pull-up resistors on the SDA and SCL lines if your breakout board lacks them, and keep wire runs short.
Frequently Asked Questions
How do you wire a light sensor to a 5V Arduino Uno if it is a 3.3V I2C module?
You must use a bidirectional logic level shifter (like the BSS138-based modules sold by SparkFun or Adafruit) between the Arduino's 5V SDA/SCL pins and the BH1750's 3.3V pins. Wiring a 3.3V I2C sensor directly to a 5V Arduino Uno will overstress the sensor's internal ESD protection diodes, eventually leading to I2C bus lockups or permanent silicon failure. Power the module from the Arduino's 3.3V output pin, but ensure the logic signals are shifted.
How do you wire a light sensor for outdoor use without blinding the reading?
Direct sunlight will saturate both LDRs and the BH1750 (which maxes out around 65,535 Lux, while direct noon sun hits 100,000+ Lux). For outdoor weather stations, mount the sensor inside an IP65-rated enclosure and cover the aperture with a PTFE (Teflon) diffuser disk. PTFE provides a near-perfect Lambertian cosine response, scattering the light evenly and attenuating peak intensity just enough to keep the reading within the sensor's linear range while still tracking cloud cover and solar angle accurately.
How do you wire a light sensor to trigger a relay directly without a microcontroller?
If you don't want to write code, use an LM393 comparator module paired with an LDR. Wire the LDR voltage divider to the comparator's non-inverting input (+), and a 10kΩ trimpot to the inverting input (-). When the light level crosses the threshold set by the trimpot, the LM393's open-collector output pulls low, which can drive a small signal transistor (like a 2N2222) to switch a 5V relay coil. This is the exact circuit used in cheap off-the-shelf solar garden lights.






