Digital ChallengeArduino × Processing
P2Project module

Light Sensor — Photoresistor (LDR)

Read the brightness of the room and paint it on screen.

Use a photoresistor in a voltage divider, read it with analogRead() (0–1023), and build a Processing display whose bar and background react to light in real time.

2 × 45 min difficultyPhysicsMathematicsComputer Science
Light Sensor — Photoresistor (LDR)
analogRead()Voltage dividermap()Dynamic colour
01Before you start

What you’ll learn

By the end of this module you can…
  • 1Explain how the resistance of an LDR changes with light.
  • 2Build a voltage divider and calculate its output voltage.
  • 3Convert between voltage and the 10-bit ADC value (0–1023).
  • 4Visualize a single sensor value as text, a bar and a background colour.
  • 5Calibrate a sensor by measuring its minimum and maximum in real conditions.
Key vocabulary
LDR / photoresistor
A resistor whose resistance falls when light shines on it (≈ 1 MΩ in darkness, ≈ 1 kΩ in bright light).
Voltage divider
Two resistors in series; the voltage in the middle depends on their ratio.
ADC
Analog-to-Digital Converter: turns 0–5 V into a number 0–1023 (10-bit) on the Uno.
analogRead()
Arduino function that reads an analog pin and returns 0–1023.
Calibration
Measuring real minimum and maximum values so your scale fits reality.
02Kit list

Required equipment

ComponentQtyPurpose
Arduino Uno1Microcontroller
Photoresistor (LDR)1Light sensing
10 kΩ resistor1Voltage divider
Breadboard1Component connection
Jumper wires~5Component connection
USB cable1Connection to computer
Processing IDE1Data visualization
03Theory

Theoretical Background

How does a photoresistor work?

An LDR (Light Dependent Resistor) is made of a semiconductor. Light gives its electrons enough energy to conduct, so in bright light the resistance decreases; in darkness it increases.

The voltage divider

Arduino can’t measure resistance directly — only voltage. So we put the LDR in series with a fixed 10 kΩ resistor. The voltage at the junction (connected to A0) changes with light.

Formula
V_A0 = 5 V × R_fixed / (R_LDR + R_fixed)
In our wiring the LDR is on the 5V side and the 10 kΩ resistor on the GND side. More light → smaller R_LDR → higher voltage → higher reading.
Formula
reading = V_A0 / 5 V × 1023
The ADC converts the voltage into a number from 0 to 1023.
InteractiveVoltage divider labChange the light level and follow the signal all the way to the screen.
Fixed resistor
5VLDR3.8 kΩA03.63 V10.0 kΩGND
R_LDR
3.8 kΩ
V at A0
3.63V
analogRead
742
Processing window · background(map(value, 0, 1023, 0, 255))
Light Intensity: 742
04Hands-on

Exercise 1 — Circuit Setup & Arduino Code

LDR + 10 kΩ voltage divider connected to A0.
LDR + 10 kΩ voltage divider connected to A0.
ComponentConnects to
LDR (leg 1)5V
LDR (leg 2)A0 and 10 kΩ resistor (leg 1)
10 kΩ resistor (leg 2)GND
Tip
The junction between the LDR and the resistor connects to A0.
LightSensor.inoArduino · C++
const int sensorPin = A0;

void setup() {
  Serial.begin(9600);
}

void loop() {
  int lightValue = analogRead(sensorPin);   // 0 (dark) … 1023 (bright)
  Serial.println(lightValue);
  delay(200);
}
05Hands-on

Exercise 2 — Processing Visualization

Start simple with a text display, then upgrade to the robust serialEvent() version with a bar.

LightText.pde — text displayProcessing · Java
import processing.serial.*;

Serial myPort;
int lightLevel = 0;

void setup() {
  size(400, 200);
  myPort = new Serial(this, Serial.list()[0], 9600);
}

void draw() {
  background(255);
  fill(0);
  textSize(20);
  text("Light Intensity: " + lightLevel, 50, 100);

  if (myPort.available() > 0) {
    String val = myPort.readStringUntil('\n');
    if (val != null) lightLevel = int(trim(val));
  }
}
LightBar.pde — bar + serialEventProcessing · Java
import processing.serial.*;

Serial myPort;
int lightLevel = 0;

void setup() {
  size(400, 200);
  myPort = new Serial(this, Serial.list()[0], 9600);
  myPort.bufferUntil('\n');
}

void draw() {
  background(255);
  fill(0);
  textSize(20);
  text("Light Intensity: " + lightLevel, 50, 50);

  fill(0, 100, 255);
  rect(50, 100, map(lightLevel, 0, 1023, 0, 300), 30);
}

void serialEvent(Serial p) {
  String val = p.readStringUntil('\n');
  if (val != null) {
    val = trim(val);
    if (val.matches("\\d+")) lightLevel = int(val);
  }
}
Make it react
Replace background(255) with background(map(lightLevel, 0, 1023, 0, 255)); — the window now gets darker when the room gets darker.
06From the original handbook

Student tasks

Basic
Display the numeric light value
Intermediate
Draw a coloured bar proportional to the light level
Advanced
Change the background colour based on light intensity
Challenge
Log values with timestamps to a text file
07When it doesn’t work

Troubleshooting

ProblemLikely causeFix
Value stays at 0LDR not connected or open circuitCheck the LDR leg goes to 5V and the junction to A0.
Value stays at 1023Resistor missing or GND not connectedCheck the 10 kΩ resistor goes from A0 to GND.
Unstable readingsPoor breadboard contact or flickering lightsPress components in firmly; average several readings.
Range is small (e.g. 400–700)Fixed resistor doesn’t match the LDRTry 4.7 kΩ or 22 kΩ, or calibrate with map().
Golden rule
Check the physical connections first, then the code. If nothing works, unplug, close both programs, reconnect and try again.
08New in this edition

Exercises

Basic · 2Intermediate · 2Advanced · 1Challenge · 1
P2.1BasicCalculate 10 min

Voltage divider practice

With R_fixed = 10 kΩ and 5 V supply, calculate V_A0 and the expected analogRead() value when the LDR is (a) 1 kΩ (bright), (b) 10 kΩ (normal room), (c) 100 kΩ (dark).

V_A0 = 5 × 10 / (R_LDR + 10) with resistances in kΩ; reading = V_A0 / 5 × 1023.
(a) 5 × 10/11 = 4.55 V → ≈ 930 · (b) 5 × 10/20 = 2.50 V → ≈ 512 · (c) 5 × 10/110 = 0.45 V → ≈ 93
P2.2BasicPredict 5 min

Swap the parts

What happens to the readings if you swap the LDR and the 10 kΩ resistor (LDR now between A0 and GND)?

The behaviour is inverted: bright light now gives a low reading and darkness a high one, because V_A0 = 5 × R_LDR / (R_LDR + R_fixed).
P2.3IntermediateBuild 20 min

Automatic night-light

Add an LED (pin 9 + 220 Ω). Turn it on when the reading drops below a threshold of your choice and off when it is brighter. Print the state to the Serial Monitor.

NightLight.inoArduino · C++
const int sensorPin = A0;
const int ledPin = 9;
const int THRESHOLD = 300;

void setup() {
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
}

void loop() {
  int light = analogRead(sensorPin);
  bool dark = light < THRESHOLD;
  digitalWrite(ledPin, dark ? HIGH : LOW);
  Serial.println(light);
  delay(200);
}
P2.4IntermediateCode 15 min

Smooth dimming with PWM

Instead of on/off, make the LED on pin 9 get brighter as the room gets darker, using analogWrite() (0–255).

Pin 9 supports PWM (~). Use map(light, 0, 1023, 255, 0) — note the reversed output range.
solutionArduino · C++
void loop() {
  int light = analogRead(A0);
  int brightness = map(light, 0, 1023, 255, 0);
  analogWrite(9, constrain(brightness, 0, 255));
  Serial.println(light);
  delay(50);
}
P2.5AdvancedCode 20 min

Calibrate with min/max

Your sensor never reaches 0 or 1023. Make Processing remember the smallest and largest values it has seen and map the bar between those, so it always uses the full width. Press R to reset calibration.

Calibration additionsProcessing · Java
int lo = 1023, hi = 0;

// in serialEvent(), after lightLevel is updated:
lo = min(lo, lightLevel);
hi = max(hi, lightLevel);

// in draw():
float w = (hi > lo) ? map(lightLevel, lo, hi, 0, 300) : 0;
rect(50, 100, w, 30);
text("calibrated: " + lo + " – " + hi, 50, 170);

void keyPressed() {
  if (key == 'r' || key == 'R') { lo = 1023; hi = 0; }
}
P2.6ChallengeDesign 40 min

Sunrise logger

Design an experiment that measures how light levels change near a window during a whole school day. Log a value every minute with a timestamp, then plot a graph. At what time is your classroom brightest? Does it depend on the weather?

Open-ended. Use delay(60000) or millis() on Arduino (or a timer in Processing), log with PrintWriter, and plot in Processing or a spreadsheet. Discuss clouds, blinds, artificial lighting, and the direction the window faces.
09Check yourself

Self-check quiz

Progress
0/5 answered · 0 correct
  1. Q01

    In bright light, an LDR’s resistance…

  2. Q02

    What range does analogRead() return on the Arduino Uno?

  3. Q03

    Why do we need the fixed 10 kΩ resistor?

  4. Q04

    An analog reading of 512 corresponds to roughly…

  5. Q05

    The reading is always 1023. The most likely cause is:

10Beyond the classroom

Real world & extensions

Where this is used
automatic street lights and night-lightsphone screen auto-brightnesscamera exposure meterssolar panel sun trackerssmart greenhouse lightingsecurity / beam-break alarms
Extension ideas
  • +Add an LED that automatically reacts to light intensity
  • +Build an automatic night-light system
  • +Use two LDRs for directional light detection (sun tracker)
  • +Send the data over WiFi for remote monitoring
  • +Build a smart-home lighting prototype
  • +Show data on an OLED or LCD screen
  • +Log light intensity for long-term analysis
Cross-curricular connections
SubjectConnection
PhysicsSemiconductors, resistance, voltage dividers
MathematicsRatios, linear scaling, non-linear curves
BiologyPhotosynthesis and daylight cycles
Computer ScienceAnalog input, data validation
11Think about it

Reflection & conclusion

?1

Which devices you own adjust themselves to light? How do you think they do it?

?2

The LDR response is not linear. Why might that matter when you build a “lux meter”?

?3

How could automatic lighting reduce your school’s energy use?

Conclusion

You learned how analog sensors and voltage dividers work, and how to turn a changing resistance into a live visualization. By combining the Arduino Uno with Processing you built an interactive light meter — the same idea behind auto-brightness screens and smart street lighting.