Measuring Light with an LDR
| Difficulty | Beginner |
|---|---|
| Category | Sensors |
| Supported boards | Arduino Uno R3 and compatible ATmega328P boards |
| Time needed | 40 minutes |
| Prerequisites | Read a Potentiometer |
| Tested board | Not yet verified |
| Tested on | Not yet verified |
| Arduino IDE version | Not yet verified |
| Library | Not yet verified |
What it does
A light-dependent resistor is a resistor whose value falls as light falls on it. In the dark it might be hundreds of kilohms; in bright light, a few hundred ohms.
That is the whole part. It has no electronics, no output pin and no polarity, and it does not produce a voltage on its own. Which raises the question this tutorial really answers: an Arduino measures voltage, so how do you read a resistance?
The technique is the same one the potentiometer used, and once you have it you can read a thermistor, a soil moisture probe, a force-sensing resistor and half a dozen other parts that work the same way.
Before you begin
Required parts and tools
- An Arduino Uno or compatible board.
- One LDR. The Siratek part is a bare two-legged photoresistor, not a module.
- One 10 kilohm resistor. This is required, not optional.
- A breadboard and three jumper wires.
Why you need the fixed resistor
An analog pin measures voltage. The LDR changes resistance. On its own, a changing resistance produces no voltage for the pin to read.
Pairing it with a fixed resistor makes a voltage divider: the two share the 5 V between them, in proportion to their resistances, and the point between them moves as the light changes. That junction is what you connect to A0.
With the LDR on the 5 V side and the fixed resistor to ground:
- Bright light: LDR resistance is low, so it drops little, and the junction sits high. The reading rises.
- Darkness: LDR resistance is high, so it drops most of the 5 V, and the junction falls. The reading drops.
Swap the two components and the relationship inverts. Neither is wrong; just know which you built.
Without the fixed resistor, A0 is connected to 5 V through the LDR and to nothing else. It will read near maximum whatever the light does, which looks exactly like a broken sensor.
Choosing the fixed resistor
10 kilohms is the usual choice and works for ordinary indoor light. The divider is most sensitive when the fixed resistor is near the LDR’s resistance in the conditions you care about, so for detecting darkness a larger value gives more useful spread, and for bright sunlight a smaller one does.
Start at 10 k. Change it only if your readings bunch up at one end of the range.
It is not a light meter
An LDR gives you more light and less light, not lux. Readings vary between individual parts, with temperature, and with how the component is angled. Anything you build should compare against a threshold you measured in place, never against a number copied from a tutorial, including this one.
Pin reference
| Pin or label | Connect to | Purpose | Notes |
|---|---|---|---|
| LDR, either leg | Uno 5V |
Top of the divider | An LDR has no polarity |
| LDR, other leg | The junction, and A0 |
The point that moves | Also goes to the fixed resistor |
| 10 k resistor, one end | The junction | Bottom of the divider | Same row as the LDR’s lower leg |
| 10 k resistor, other end | Uno GND |
Return path |
Wiring steps
- Unplug the USB cable.
- Put the LDR across two breadboard rows. Either way round is fine.
- Run a wire from one LDR leg to
5V. - Put one end of the 10 k resistor in the same row as the LDR’s other leg. This shared row is the junction.
- Run the resistor’s other end to
GND. - Run a wire from the junction row to
A0. - Check that the junction row really has three things in it: the LDR leg, the resistor leg and the wire to
A0. IfA0is anywhere else, the reading will not move. - Plug the USB cable back in.
Software and libraries
No library is required. analogRead() is part of the Arduino core.
Minimal working example
// Siratek: reading an LDR through a voltage divider.
const int ldrPin = A0;
void setup() {
Serial.begin(9600);
Serial.println("Siratek: cover the sensor and watch the reading.");
}
void loop() {
int reading = analogRead(ldrPin);
Serial.print("light = ");
Serial.println(reading);
delay(200);
}
Run this first and do two things: cup your hand over the LDR, and shine a phone torch on it. Write down the two numbers. Those are the real limits in your room, and every threshold you choose should sit between them.
A light that comes on in the dark
// Siratek: switching an LED on when the light falls below a threshold.
const int ldrPin = A0;
const int ledPin = 9;
// Measure your own. This is the midpoint between covered and lit, in your room.
const int darkBelow = 400;
void setup() {
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
}
void loop() {
int reading = analogRead(ldrPin);
bool dark = reading < darkBelow;
digitalWrite(ledPin, dark ? HIGH : LOW);
Serial.print("light = ");
Serial.print(reading);
Serial.println(dark ? " dark" : " lit");
delay(200);
}
Right on the threshold this will flicker, switching on and off as the reading wavers by a count or two. The fix is to use two thresholds rather than one: switch on below 380 and off above 420, so the reading has to move meaningfully before anything changes. That gap is called hysteresis, and it is worth adding to anything that drives a relay or a light.
Expected result
A reading between 0 and 1023 that rises in bright light and falls when covered, with the wiring above.
Typical indoor values might run from a few dozen covered to the high hundreds under a lamp, but do not treat those as targets. What matters is that your own numbers move a long way between covered and lit. A reading that barely shifts means the divider is wrong, not that the room is evenly lit.
The value also wavers by a count or two when nothing changes. That is genuine measurement noise, and averaging eight readings smooths it, exactly as in the potentiometer tutorial.
Troubleshooting
| Symptom | Likely cause | Check or fix |
|---|---|---|
| Always near 1023 | Fixed resistor missing | Fit the 10 k from the junction to GND |
| Always near 0 | 5V not connected, or LDR open |
Check the top of the divider |
| Reading never moves | A0 not on the junction row |
It must share a row with both components |
| Reading moves the wrong way | LDR and resistor swapped | Expected; swap them or invert the test |
| Only a small range | Fixed resistor far from the LDR’s range | Try 4.7 k in bright light, 47 k for darkness |
| Flickers at the threshold | Single threshold | Use two, with a gap between them |
| Jumps around constantly | Loose leg, or mains flicker nearby | Reseat, then average several readings |
| Different from a friend’s board | Parts vary a great deal | Normal; always measure your own thresholds |
Next steps
- Easier first: Read a Potentiometer, which is the same divider with a knob instead of light.
- Related products: the DHT11 for temperature and humidity, or the flame sensor, which uses this same arrangement with an infrared-sensitive part.
- Next project: a night light that fades up as the room darkens, using PWM rather than a simple on and off.
Sources and attribution
All text, wiring guidance and code in this tutorial are original Siratek work.
- Arduino core documentation for
analogRead().
LDR resistance ranges vary widely between parts and are given here as rough orders of magnitude. Every threshold in this tutorial is presented as something the reader must measure rather than copy. No third-party tutorial text, diagrams, photographs or code has been reproduced here.

