Wire an LDR straight to an Arduino analog pin and you will read… nothing useful. A photoresistor changes resistance with light — from around 1 kΩ in bright light to 1 MΩ in darkness on a typical GL5528 — but an ADC reads voltage. The bridge between the two is a voltage divider, and the entire design question is a single number: which fixed resistor do you pair with the LDR? Pick it badly and your sensor saturates at one end of its range; pick the geometric mean and the output swings cleanly across the full light range. This calculator picks it for you.
The divider equation
Two resistors in series between supply and ground; the output is tapped between them:
V_out = V_supply × R_lower / (R_upper + R_lower)
Put the LDR in either leg:
- LDR on the bottom (pin to ground): brighter light → lower LDR resistance → lower V_out.
- LDR on top (supply to pin): brighter light → higher V_out.
Both are valid; choose whichever polarity makes your code read naturally. The fixed resistor also bounds the worst-case current: with a 33 kΩ fixed resistor on 5 V, the divider draws at most ~150 µA even with the LDR fully bright — negligible for battery projects.
Why the geometric mean maximises swing
Suppose the LDR ranges from R_bright to R_dark. If you choose a fixed
resistor near R_bright, the divider barely moves through the entire dim
half of the range (the LDR term dominates); choose near R_dark and the
bright half is squashed instead. The output swing between the two extremes
is maximised when:
R_fixed = √(R_bright × R_dark)
For a GL5528: √(1,000 × 1,000,000) ≈ 31.6 kΩ → nearest E12 value 33 kΩ. That single 33 kΩ is why the value appears in virtually every Arduino photocell tutorial. Because LDR resistance is roughly log-linear in lux (the datasheet “gamma” parameter), the geometric mean also centres the transition on a logarithmic light axis — matching how illumination actually varies between indoor shade and daylight.
Worked example
GL5528 on a 5 V Arduino Uno, LDR on top, 33 kΩ to ground:
- Bright (R_ldr ≈ 1 kΩ): V_out = 5 × 33k/(1k + 33k) ≈ 4.85 V → ADC ≈ 993
- Dark (R_ldr ≈ 1 MΩ): V_out = 5 × 33k/(1M + 33k) ≈ 0.16 V → ADC ≈ 33
- Room light (R_ldr ≈ 10 kΩ): V_out = 5 × 33k/43k ≈ 3.84 V → ADC ≈ 785
The usable swing is ~4.7 V — nearly the whole ADC range, which is exactly what the geometric-mean choice buys you.
Common LDR parts and their resistor picks
| LDR part | Bright Ω (approx.) | Dark Ω (approx.) | Geometric mean | Nearest E12 |
|---|---|---|---|---|
| GL5528 | 1 kΩ | 1 MΩ | 31.6 kΩ | 33 kΩ |
| GL5516 | 2 kΩ | 500 kΩ | 31.6 kΩ | 33 kΩ |
| GL5539 | 5 kΩ | 5 MΩ | 158 kΩ | 150 kΩ |
| NORP12 | 100 Ω | 10 MΩ | 31.6 kΩ | 33 kΩ |
Datasheet values are typical, not guaranteed — individual GL55xx units vary substantially (they are cheap cadmium-sulfide parts), which is why software calibration (below) beats hard-coded thresholds.
The complete Arduino circuit
5V ──┬── [LDR] ──┬── [33 kΩ] ── GND
│
A0 ──► analog pin
│
100 nF ── GND (optional flicker filter)
int raw = analogRead(A0); // 0-1023 on a 5 V Uno
float volts = raw * (5.0 / 1023.0); // high = bright with LDR on top
On a 3.3 V board (ESP32, RP2040), only the reference changes:
float volts = raw * (3.3 / 4095.0); // 12-bit ADC on ESP32
The recommended series resistor is the same at any supply voltage — the divider ratio, not the absolute voltage, is what the geometric mean optimises.
Design tips and edge cases
- Round to E12 and don’t agonise. 10k, 15k, 22k, 33k, 47k, 68k, 100k — anything within about 2× of the geometric mean preserves most of the swing, and your code thresholds the reading anyway.
- Bias deliberately for day/night sensors. If you only care about one transition (dusk detection), skew the fixed resistor toward the dark resistance so the ADC spends its resolution around the light level you actually switch on.
- Filter mains flicker. Indoor lighting flickers at 100/120 Hz; a 100 nF capacitor from the analog pin to ground low-pass filters it. Alternatively average 8-16 readings in software.
- Calibrate in software, not in constants. Sample a known-dark and known-bright reading at startup (or store a rolling min/max) and compute thresholds from those. Unit-to-unit LDR spread, enclosure optics, and aging make any hard-coded ADC threshold fragile.
- LDRs are slow — by design. CdS cells respond in tens of milliseconds and “remember” light exposure briefly (the datasheet light-history effect). For anything needing speed or accuracy — flicker measurement, lux metering — use a photodiode or a digital lux sensor (BH1750, TSL2591) instead; the LDR’s niche is cheap, robust relative light sensing.
- Regulatory note: cadmium-based LDRs are restricted under the EU RoHS directive for many product categories, which is why commercial products have largely moved to photodiodes. For hobby projects they remain widely available.
References
- Arduino documentation — analogRead()
- GL55xx series photoresistor datasheet (bright/dark resistance and gamma specifications; available from part distributors)
All calculations run locally in your browser.