Light Sensor (LDR)
Anand RamaswamiShare
Welcome Back, Maker!
You have already met the LDR. Back in the analog input blog you used it to control how fast an LED blinks — covering it with your finger slowed things down, shining a light sped things up. You saw the numbers change on the Serial Monitor and felt what analog input really means.
But we never stopped to ask — what is actually happening inside that little component? Why does covering it change anything at all?
That is where this blog starts. And by the end of it you will have built three projects that actually do something useful with light — not just blink differently because of it.
What Is an LDR?
LDR stands for Light Dependent Resistor.
A resistor is a component that resists the flow of electricity. Most resistors have a fixed resistance — they always resist by the same amount no matter what. But an LDR is different. Its resistance changes depending on how much light falls on it.
More light → resistance drops → more current flows → higher voltage at the pin → higher ADC value.
Less light → resistance rises → less current flows → lower voltage at the pin → lower ADC value.
That changing voltage is what your Arduino reads using analogRead(). The LDR on your NanoMake Pro is connected to pin A7, and you can start reading it right now without connecting anything extra.
How Does It Actually Work?
Inside an LDR is a material called a photoconductor. In the dark, the electrons in this material are tightly held in place and the resistance is very high — sometimes over a megaohm. But when light hits the material, photons knock those electrons loose and they become free to carry current. The more light, the more free electrons, the lower the resistance.
This is called the photoconductive effect. It sounds complicated but the takeaway is simple — light loosens electrons, lower resistance, higher voltage, higher number from analogRead(). Darkness tightens them back up, higher resistance, lower voltage, lower number.
That is the whole story.
Project 1: Light Intensity Indicator on Serial Monitor
Before building anything that reacts to light, let's just watch the LDR values in real time. This project reads the LDR and prints both the raw ADC value and the actual voltage to the Serial Monitor — so you can see exactly what your sensor is doing as light levels change around it.
Code:
|
void setup() { Serial.begin(9600); } void loop() { int adcValue = analogRead(A7); float voltage = (adcValue / 1023.0) * 5.0; Serial.print("Light Level: "); Serial.print(adcValue); Serial.print(" Voltage: "); Serial.print(voltage); Serial.println(" V"); delay(300); } |
Explanation:
Serial.begin(9600) starts communication between the Arduino and your computer so data can appear on the Serial Monitor.
analogRead(A7) reads the current voltage on pin A7 — where the LDR is connected — and converts it into a number between 0 and 1023. This is the ADC doing its job. Bright light gives a high number. Darkness gives a low number.
(adcValue / 1023.0) * 5.0 converts that raw number back into an actual voltage. Dividing by 1023.0 gives the fraction of the full range. Multiplying by 5.0 scales it to the 0V to 5V range. The decimal points are important — without them Arduino does whole-number division and rounds everything to 0 or 1.
float voltage stores the result as a decimal number so you can see values like 3.47V instead of just 3V.
Serial.print() and Serial.println() display both values side by side on the Serial Monitor. The difference between them is that Serial.println() moves to the next line after printing — so each reading appears on its own line instead of running together.
delay(300) waits 300 milliseconds before reading again. This slows the output down enough to actually read it on screen.
Open the Serial Monitor and try covering the LDR with your finger, shining your phone torch at it, and slowly moving between light and shadow. Watch both numbers change smoothly as the light level changes. This is the sensor doing its job — translating light into numbers your Arduino can work with.
Output:

From Reading Light to Reacting to It
Watching numbers on a screen is useful for understanding the sensor. But what if you could make something actually respond to those numbers — automatically, without pressing any button?
That is the idea behind automation. The sensor reads the environment. The code makes a decision. Something happens.
The most classic example of this is a street light. In the real world, street lights turn on by themselves when it gets dark and turn off when the sun comes up. Nobody presses a switch. The light level decides everything.
Let's build exactly that.
Project 2: Automatic Street Light
In this project the LED turns ON automatically when the environment gets dark and turns OFF when there is enough light. Just like a real street light — no buttons, no manual control. The LDR is in charge.
|
void setup() { pinMode(9, OUTPUT); Serial.begin(9600); } void loop() { int adcValue = analogRead(A7); Serial.print("Light Level: "); Serial.println(adcValue); if (adcValue < 400) { digitalWrite(9, HIGH); // Dark — LED ON } else { digitalWrite(9, LOW); // Bright — LED OFF } delay(200); } |
Explanation:
pinMode(9, OUTPUT) sets pin 9 as an output so it can control the LED.
analogRead(A7) reads the current light level from the LDR. The value will be somewhere between 0 and 1023 depending on how much light is falling on the sensor.
Serial.println(adcValue) prints the reading to the Serial Monitor so you can watch the value in real time while the LED reacts. This is really useful for finding the right threshold for your specific environment.
if (adcValue < 400) is the decision. If the light level drops below 400 — meaning it is getting dark — the LED turns ON. If the light level is 400 or above the LED turns OFF.
The number 400 is a threshold. It is the boundary between what counts as dark and what counts as bright. Your room might be different from someone else's room — if your LED turns on too easily or not easily enough, change 400 to a higher or lower number until it feels right. Open the Serial Monitor, cover the LDR slowly with your hand, and watch where the value settles in darkness. Use that number as your threshold.
digitalWrite(9, HIGH) turns the LED ON when it is dark. digitalWrite(9, LOW) turns it OFF when there is enough light.
delay(200) gives the loop a short pause between readings so the LED does not flicker rapidly if the light level is hovering right around the threshold.
Cover the LDR slowly and watch the LED come on. Uncover it and watch it switch off. That is an automatic sensor-driven system — and the exact same logic runs in every street light, security light, and automatic night lamp in the world.
Output:

Making It Smoother
The automatic street light works brilliantly but it only has two states — fully ON or fully OFF. That is fine for a street light but what if you wanted something more natural? Something that gets gradually brighter as the room gets darker and gradually dims as light comes back in?
That is where PWM comes back into the picture. Instead of digitalWrite() giving us ON or OFF, analogWrite() lets us set any brightness level in between. And instead of checking a threshold, we just map the LDR value directly to a brightness value — so the LED brightness constantly tracks the light level in real time.
Project 3: Smooth Automatic Night Lamp
In this project the LED brightness adjusts smoothly and continuously based on the light level — the darker it gets, the brighter the LED glows. It is like a night lamp that always sets itself to exactly the right brightness for the room.
Code:
|
void setup() { pinMode(9, OUTPUT); Serial.begin(9600); } void loop() { int adcValue = analogRead(A7); int brightness = map(adcValue, 0, 1023, 255, 0); analogWrite(9, brightness); Serial.print("Light Level: "); Serial.print(adcValue); Serial.print(" Brightness: "); Serial.println(brightness); delay(100); } |
Explanation:
analogRead(A7) reads the current light level from the LDR. Bright light gives a high value close to 1023. Darkness gives a low value close to 0.
map(adcValue, 0, 1023, 255, 0) is where the magic happens. The map() function converts one range of numbers into another range proportionally. Normally map() would convert 0 to 0 and 1023 to 255 — but notice the output range is written as 255, 0 instead of 0, 255. This flips the mapping. So when the LDR reads 0 — darkness — the brightness becomes 255 — fully bright. When the LDR reads 1023 — bright light — the brightness becomes 0 — LED off. The sensor and the LED go in opposite directions, which is exactly what you want for a night lamp.
analogWrite(9, brightness) sends the PWM signal to the LED. The LED instantly changes to the brightness level the map() function just calculated. As the light level changes smoothly, the brightness changes smoothly with it — no sudden jumps, no threshold, just a continuous response.
Serial.print() and Serial.println() print both the light level and the current brightness side by side. Open the Serial Monitor and slowly cover the LDR with your hand. Watch the light level drop and the brightness rise at the same time.
delay(100) gives the loop a 100 millisecond pause. This is short enough that the response feels instant but long enough to keep the Serial Monitor readable.
Slowly move your hand over the LDR and watch the LED get brighter and brighter as you cover it. Move it away and the LED dims back down. The response is smooth and continuous — it follows your hand like a shadow.
Output:

LDRs Are Everywhere
Once you know what an LDR does you start seeing them everywhere.
The screen brightness on your phone adjusts automatically based on the light around you — there is a small light sensor doing exactly what your LDR is doing, reading the environment and sending a number to the processor. That is the same idea.
Solar-powered garden lights sit in sunlight all day charging their batteries. When evening comes and the light level drops below a threshold, the light turns on automatically. There is a small LDR-based circuit inside making that decision. That is the same idea as Project 2.
Camera exposure systems measure how much light is available and adjust the shutter speed and aperture accordingly. The light measurement starts with a sensor that changes its output based on light intensity. Same idea again.
Final Thoughts
The LDR is one of those sensors that feels simple — and it is. But simple does not mean limited. With just one sensor and a few lines of code you have built a live light meter, an automatic street light, and a smooth self-adjusting night lamp.
The LDR does not know what it is attached to. It just changes its resistance based on light. Your code is what decides what that change means — and what to do about it.
That is the skill you are building here. Reading the world, making a decision, taking action.
Keep experimenting, keep building, and keep making.