A light dependent resistor (LDR), also called a photoresistor, is one of the simplest and cheapest light sensors you can use with an ESP32. Its resistance changes with the amount of light falling on it -- the more light, the lower the resistance. LDRs are perfect for basic tasks like detecting whether it is light or dark, but they are not calibrated for precise lux measurements. If you need exact light intensity values, consider the BH1750 instead.
The best part: you do not need any library. Just analogRead() and a resistor.
Alternatives: If you need calibrated lux readings or want to avoid using an ADC pin, the BH1750 is a digital I2C light sensor that outputs precise values directly. See our light sensor comparison for help choosing.
This guide uses the ESP32-WROOM-32 DevKit. Pin labels may differ on your board -- check your board's pinout diagram.
🔗Key Specs
| Parameter | Value |
|---|---|
| Light resistance | $\approx 1\,\text{k}\Omega$ (bright light) |
| Dark resistance | $\approx 1\,\text{M}\Omega$ (darkness) |
| Supply voltage | Works with $3.3\,\text{V}$ or $5\,\text{V}$ |
| Output | Analog (variable resistance) |
| Calibration | None (relative readings only) |
| Library needed | None |
LDR resistance values vary between models and manufacturers. The values above are typical for common CdS (cadmium sulfide) photoresistors.
🔗What You'll Need
| Component | Qty | Notes | Buy |
|---|---|---|---|
| ESP32 dev board | 1 | AliExpress | Amazon.de .co.uk .com | |
| LDR (photoresistor) | 1 | AliExpress | Amazon.de .co.uk .com | |
| 10k ohm resistor | 1 | For the voltage divider | AliExpress | Amazon.de .co.uk .com |
| Breadboard | 1 | AliExpress | Amazon.de .co.uk .com | |
| Jumper wires | 4 | AliExpress | Amazon.de .co.uk .com |
Links marked Amazon/AliExpress are affiliate links. We may earn a small commission at no extra cost to you.
🔗Wiring
Since the LDR is a variable resistor (not a voltage source), you need a voltage divider circuit to convert the changing resistance into a changing voltage that the ESP32's analog-to-digital converter (ADC) can read.
| Connection | Pin / Component |
|---|---|
| LDR leg 1 | 3.3V |
| LDR leg 2 | GPIO 34 and one leg of a 10 kΩ resistor |
| Other leg of 10 kΩ resistor | GND |
This creates a voltage divider between the LDR (top) and the $10\,\text{k}\Omega$ fixed resistor (bottom), with GPIO 34 reading the voltage at the midpoint.
Why GPIO 34? GPIOs 34--39 on the ESP32 are input-only pins, which makes them ideal for analog readings. More importantly, these pins belong to ADC1 (GPIOs 32--39), which works reliably even when WiFi is active. ADC2 (GPIOs 0, 2, 4, 12--15, 25--27) conflicts with WiFi and should be avoided if your project uses wireless connectivity.
Pin labels, GPIO numbers, and ADC channels vary between ESP32 boards. Always check your board's pinout diagram.
🔗The Voltage Divider
The voltage at the midpoint of a voltage divider is given by:
$$V_{out} = V_{in} \cdot \frac{R_2}{R_1 + R_2}$$
In our circuit:
- $V_{in} = 3.3\,\text{V}$
- $R_1$ = LDR resistance (varies with light)
- $R_2 = 10\,\text{k}\Omega$ (fixed resistor)
When it is bright (LDR resistance is low, say $1\,\text{k}\Omega$):
$$V_{out} = 3.3 \cdot \frac{10{,}000}{1{,}000 + 10{,}000} = 3.3 \cdot \frac{10{,}000}{11{,}000} \approx 3.0\,\text{V}$$
When it is dark (LDR resistance is high, say $200\,\text{k}\Omega$):
$$V_{out} = 3.3 \cdot \frac{10{,}000}{200{,}000 + 10{,}000} = 3.3 \cdot \frac{10{,}000}{210{,}000} \approx 0.16\,\text{V}$$
So the voltage increases with more light and decreases in darkness. The ADC converts this voltage into a digital value.
🔗Required Libraries
None. The analogRead() function is built into the Arduino framework and requires no additional libraries.
🔗Code Example
This sketch reads the analog value from the LDR voltage divider and prints it to the Serial Monitor.
#define LDR_PIN 34 // GPIO 34 (ADC1 channel, input-only)
void setup() {
Serial.begin(115200);
// No need to set pinMode for analogRead on ESP32
}
void loop() {
int rawValue = analogRead(LDR_PIN);
// ESP32 ADC is 12-bit: 0 to 4095
// Higher value = more light (with this wiring)
float voltage = rawValue * (3.3 / 4095.0);
Serial.print("ADC: ");
Serial.print(rawValue);
Serial.print(" | Voltage: ");
Serial.print(voltage, 2);
Serial.print(" V | Light level: ");
if (rawValue > 3000) {
Serial.println("Bright");
} else if (rawValue > 1500) {
Serial.println("Medium");
} else if (rawValue > 500) {
Serial.println("Dim");
} else {
Serial.println("Dark");
}
delay(500);
}🔗ADC Non-Linearity on the ESP32
The ESP32's built-in ADC is not perfectly linear, especially near the extremes ($0\,\text{V}$ and $3.3\,\text{V}$). Readings near the bottom and top of the range may be less accurate than readings in the middle.
For an LDR, this usually does not matter much since you are looking at relative light levels, not precise voltage measurements. However, if you need better ADC accuracy for other projects, be aware that:
- The ADC tends to saturate early (readings may hit 4095 before the actual voltage reaches $3.3\,\text{V}$)
- Readings near $0\,\text{V}$ may not reach exactly 0
- Espressif provides ADC calibration functions in the ESP-IDF framework for improved accuracy
🔗How It Works
An LDR is made of a semiconductor material (typically cadmium sulfide, CdS) whose resistance decreases when photons strike it. The more light that hits the sensor, the more charge carriers are generated in the material, and the lower the resistance becomes.
By placing the LDR in a voltage divider, the changing resistance is converted to a changing voltage. The ESP32's ADC reads this voltage and converts it to a 12-bit digital value (0--4095). Your code can then use this number to make decisions.
Note: LDRs are not calibrated and their characteristics vary from unit to unit. They are excellent for detecting relative light changes (bright vs. dark, light trending up or down) but are not suitable for measuring precise lux values. For calibrated light measurements, use a digital sensor like the BH1750.
🔗Troubleshooting
| Problem | Possible Cause | Solution |
|---|---|---|
| Always reads 0 | LDR not connected or wrong pin | Check wiring; verify you are using an ADC-capable GPIO |
| Always reads 4095 | Missing fixed resistor or short circuit | Ensure the 10 kΩ resistor is in the circuit |
| Readings do not change | LDR is dead or not light-sensitive | Try covering and uncovering the LDR; replace if no change |
| Erratic readings | Floating pin or loose connection | Check all connections; ensure solid contact on breadboard |
| Reads 0 when WiFi is active | Using an ADC2 pin | Switch to an ADC1 pin (GPIOs 32--39) |
🔗Next Steps
- Use the LDR reading to control the brightness of an LED with PWM
- Build a simple light-activated switch using a relay
- Log light levels over time to track sunrise and sunset patterns
- Combine with a DS18B20 temperature sensor for a basic weather station