Skip to Content

How to detect light using an Arduino

How to detect light using an Arduino

In this tutorial, we will explore how to detect light using an Arduino. Light detection is a fundamental aspect of many projects, ranging from automatic light switches to regulated lighting systems. By understanding the principles behind light detection, you can create innovative applications that respond to changes in light levels.

Using an Arduino board, we can easily interface with various sensors and components to detect light. One of the most commonly used sensors for light detection is the photoresistor, also known as a light-dependent resistor (LDR). By measuring the resistance of the photoresistor, we can determine the intensity of light falling on it.

In this blog post, we will guide you through the process of building a light detector using an Arduino and a photoresistor. We will also explore how to expand on this basic setup to create more advanced projects, such as an automatic light switch, regulated lighting, and even a light-controlled servo.

Whether you are a beginner or an experienced Arduino enthusiast, this tutorial will provide you with the necessary knowledge to start working with light detection. So, let’s dive in!

Required Parts

Below you will find the components required to build this project. If you already have resistors, LEDs and trimmers you obviously won’t need the kits listed. Otherwise, they are worth buying, since it will be cheaper than buying individual parts.

Arduino Uno

Dupont wire set

Dupont Wire Set

Half_breadboard56a

Breadboard

USB Data Sync cable Arduino

USB Cable for Arduino UNO

Resistor & LED kit

Variable Resistor Set

Photoresistor set

SG90 Micro Servo

Positional Servo

Makerguides.com is a participant in the Amazon Services LLC Associates Program, an affiliate advertising program designed to provide a means for sites to earn advertising fees by advertising and linking to products on Amazon.com. As an Amazon Associate we earn from qualifying purchases.

What is a Photoresistor or LDR

A photoresistor, also known as a light-dependent resistor (LDR), is a type of resistor whose resistance changes based on the amount of light it is exposed to. It is a passive component that is widely used in various light detection and control applications. The picture below shows a typical photoresistor.

Photoresistor/LDR
Photoresistor/LDR

Note that there is no polarity for the two wires. Since it is just a resistor you can use it in any orientation. For the Arduino you can also get modules with additional integrated circuitry. They allow you to adjust the sensitivity of the sensor, but we will build that ourselves.

How does a Photoresistor work?

A photoresistor is made of a semiconductor material that is photoconductive. When light falls on the surface of the photoresistor, the photons in the light energy excite the electrons in the semiconductor material, causing them to move more freely. This movement of electrons reduces the resistance of the photoresistor.

On the other hand, when there is no light or the intensity of light is low, the semiconductor material has a higher resistance. This characteristic makes photoresistors ideal for detecting light levels.

Voltage Divider

To measure the resistance of a photoresistor and convert it into a measurable voltage, a voltage divider circuit is commonly used. It consists of two resistors connected in series. One of them being the photoresistor. The other is often a potentiometer or trimmer to adjust the sensitivity or threshold of the circuit. However, you will also find some simpler circuits that use a fixed value resistor instead of a trimmer.

The picture below show the schematics of the voltage divider we will use in the following sections.

Voltage Divider for light detection
Voltage Divider

You can see the that the photoresistor (R1) and the trimmer (R2) are connected in series between the power supply (5V) and ground (GND). The junction between the photoresistor (R1) and the trimmer is connected to an analog input pin (A0) of the Arduino. As the resistance of the photoresistor changes with the light intensity, it affects the voltage at the junction point.

The formula for calculating the output voltage of a voltage divider circuit is:

V_A0 = Vin * (R1 / (R1 + R2))

Where:

  • V_A0 is the output voltage
  • Vin (+5V) is the input voltage
  • R1 is the resistance of the photoresistor
  • R2 is the resistance of the trimmer

In the next section, you will learn how to build a light detector using this circuit and an Arduino

Building a Light Detector

In this section I show how to connect an LDR to an Arduino and output the measured light intensities on the Serial Monitor.

Building the Voltage Divider

The following diagram shows you how to connect the parts to build the voltage divider we discussed above. First we connect 5V to the positive power rail of the breadboard with a red wire. Then we connect ground (GND) the the negative power rail using a blue wire.

Voltage Divider Circuit connected to Arduino
Voltage Divider Circuit connected to Arduino

Now the voltage divider. Connect one pin of the photoresistor (LDR) with the positive power rail (red wire). The other pin needs to be connected to the center pin of the trimmer. Make sure to get that right, otherwise trimming will not work.

For the trimmer we pick a resistance value roughly equal to the resistance of the LDR under normal light conditions. In my case this is about 20K Ohms. The exact value is not overly critical.

Then we connect the junction point between trimmer and photoresistor with a yellow wire to the analog input A0 on the Arduino.

The last connection is between one of the other trimmer pins to the negative power rail (blue wire). Which pin you are using for that doesn’t matter. Finally, rotate the trimmer to its middle position and then we start writing the software.

Code for the Light Detector

The software for the light detector is very simple. We define a constant ldrPin for the pin the LDR is connected to and a variable ldrValue to store the detected brightness.

const int ldrPin = A0;

int ldrValue = 0;

void setup() {
  Serial.begin(9600);
  pinMode(ldrPin, INPUT);
}

void loop() {
  ldrValue = analogRead(ldrPin);
  Serial.print("LDR:");
  Serial.println(ldrValue);
  delay(100);
}

In the setup() function, we initiate Serial communication and then set the mode of the ldrPin to INPUT, since we are going to read from it.

In the loop() function we read the LDR value via analogRead and then print it to the Serial monitor. Now open the Serial Plotter and wave your hand across the LDR to change the amount of light it receives. You should see a fluctuating curve similar to the one below.

Serial plotter output with detected light levels
Serial Plotter showing detected light levels

If you don’t get a signal (flat line), check you circuit and make sure the trimmer is in its middle position.

The analog input returns values in the range between 0 and 2023 on an Arduino UNO. However, we usually will not see the entire range, provided the sensor is not exposed to complete darkness and extreme brightness.

To get a good sensitivity of the sensor, play with different levels of brightness/darkness and a adjust the trimmer, so that you don’t hit the minimum (0) or the maximum (1023) value. Note that these values are not calibrated in any sense. For instance if you want to measure the light intensity in Lux, you would need a light source with known intensity to calibrate the readings.


Building an Automatic Light Switch

Since we now can detect light, we can build an automatic light switch. Whenever it gets dark, we switch on the light. For this example, we add an LED with a resistor to the circuit.

Circuit for Automatic Light Switch
Circuit for Automatic Light Switch

We keep the voltage divider as it is. Just add the LED and a 220 Ohm resistor to the breadboard as shown above. Then connect the long pin of the LED with an orange wire to Pin 9 on the Arduino. Finally, connect the resistor pin with a blue wire to the negative power rail of the breadboard. Done!

Extending the code is also very easy. Just add an additional constant ledPin for the output pin the LED is connected to and set the pinMode to OUTPUT in the setup() function.

const int ldrPin = A0;
const int ledPin = 9;

int ldrValue = 0;

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

void loop() {
  ldrValue = analogRead(ldrPin);
  digitalWrite(ledPin, ldrValue > 500 ? HIGH: LOW);
  delay(1000);
}

In the loop() function we read the ldrValue as before. But then, depending on the value we switch the LED on or off using digitalWrite. If the ldrValue is greater than 500, it is pretty bright, and we switch the LED off (LOW). If the value is smaller, it is dark, and we switch the LED on (HIGH).

There you go! Now you have an automatic light that switches on when it gets dark. You can adjust the threshold for switching by changing the value of 500 to a higher or lower value (in the range from 0 to 1023). If you want to switch brighter lights, you can replace the LED by a relay and the switch your room lights, for instance.

Building a Regulated Light or Dimmer

In the previous section, we built an automatic light switch that switches the LED completely on or off. In this section, I show you, how you can regulate the brightness of the LED smoothly, depending on how bright it is. So, instead of switching the LED we are going to dim it.

Here is the code for it. We start by adding a brightness variable but leave the setup() function unchanged.

const int ldrPin = A0;
const int ledPin = 9;

int ldrValue = 0;
int brightness = 0;

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

void loop() {
  ldrValue = analogRead(ldrPin);
  brightness = map(ldrValue, 50, 300, 255, 0);
  brightness = constrain(brightness, 0, 255);
  analogWrite(ledPin, brightness);
  delay(10);
}

In the loop() function we read the ldrValue as before but then map its value to the range 255 to 0. Note that the range is inverted (255 to 0, instead of, 0 to 255). That is, because we want the LED to get brighter the darker the surroundings are.

The ldrValue has a possible range from 0 to 1023. But we only use a range of 50 to 300 in the code above. This way the LED reacts to comparatively small changes in the lighting conditions. Otherwise, it has to be very bright or dark to see any change to the LED’s brightness.

However, this causes a little problem. The map function does not guarantee that its output value is actually in the range 255 to 0. If it gets brighter or darker than the 50 or 300 in the input range, the output value will be smaller or larger than 0 or 255. But the analogWrite() function supports only the range 0 to 255. We therefor use constrain() to ensure this range.

The analogWrite() function uses PWM (Pulse Width Modulation) to affect the brightness of the LED. If you want to learn more about that have a look at our tutorial on How use Arduino to control an LED with a Potentiometer.

There we go! Now you have a regulated light that gets brighter the darker the surroundings are. If you want it less or more sensitivity, play with the values of the input range (50, 300).

Building a Light Direction Detector

We can easily modify out light detection circuit to make it a Light Direction Detector. To achieve that we simply replace the trimmer by a second LDR. As you can see below, the new circuit is still a Voltage Divider but with two LDRs.

Voltage Divider with two LDRs for light direction detection
Voltage Divider with two LDRs

Depending on which of the LDRs receives more light the voltage at A0 will increase or decrease. Note that it does not matter how much light the LDRs receive. Bright or dark the voltage at A0 will be about the same. What matters is the ratio. The larger the difference in light received by the LDRs the more A0 will change from its midpoint value of about 2.5 V.

Let’s build this circuit on the breadboard. Since it is essentially the same circuit as before, we just remove the trimmer and replace it by an LDR.

Circuit for Light Direction Detector
Circuit for a Light Direction Detector

Current flows from the positive power rail through the serial arrangement of the two LDRs to the negative power rail. As before, we take the signal (yellow wire) from the junction point and feed it to Pin A0 on the Arduino.

Below you can see the code we are going to use.

const int ldrPin = A0;
const int midValue = 500;
const int midRange = 10;

int ldrValue = 0;

void setup() {
  Serial.begin(9600);
  pinMode(ldrPin, INPUT);  
}

void loop() {
  ldrValue = analogRead(ldrPin);
  Serial.print("LDR:");
  Serial.println(ldrValue);

  if (ldrValue > midValue + midRange) {
    Serial.println("Left");
  } else if (ldrValue < midValue - midRange) {
    Serial.println("Right");
  } else {
    Serial.println("Middle");
  }

  delay(100);
}

We extend the simple light detector code from before by adding two variables. midValue is the value we expect to read from A0 when both LDRs are exposed to the same amount of light. In theory that value should be 1023/2 = 512 (half of the input range). In practice, the resistance of the two LDRs is never perfectly identical and the actual value will be slightly different.

const int midValue = 500;
const int midRange = 10;

To find that midpoint you bring both LDRs close to a light with the same angle and distance and read the ldrValue from Serial monitor. This will be your value for midValue. In my case, I read about 500, which is pretty close to the theoretical value.

The midRange constant defines what amount of deviation we except from midValue until we call a direction. It defines a buffer or tolerance. For any value in that range we will print that light is the middle position – neither to the left nor to the right.

The setup() function remains unchanged. We just initiate the Serial Monitor and set the pin mode.

In the loop() function we read ldrValue as before and then compare it with the midValue.

  if (ldrValue > midValue + midRange) {
    Serial.println("Left");
  } else if (ldrValue < midValue - midRange) {
    Serial.println("Right");
  } else {
    Serial.println("Middle");
  }

If the sensor value is greater than the midpoint value plus the tolerance, we know that the left LDR received more light and we print “Left“. If the value is smaller than the midpoint value minus the tolerance, we print “Right“. And in the remaining case, both LDRs receive roughly the same light and we print “Middle“. Note that “left” and “right” depend on which way your circuit is facing.

A final note, you can dramatically improve the directional sensitivity by placing the LDRs in tubes so that they only receive directional light. Below is a picture of my cheap hack to test that:

LDRs with and without tubes
LDRs with and without tubes

By adding the tubes I can accurately detect the direction the light comes from and in the next section I will show you how to control a servo with it.

Building a Light Controlled Servo

Positional servos are designed to rotate to a specific given angle. This makes them a natural choice to try out our light direction detector. We will be able to control the angle of the servo and make it point to the direction the light comes from.

First add the servo by connecting its power to the power rails (brown is negative and red is positive). Then connect the signal input of the servo (yellow) with an orange wire to Pin 9 of the Arduino. Done!

Circuit to control servo via light direction detector
Circuit to control servo via light direction detector


Below you will find the code to control the servo. It is a small extension of the code above. If you have difficulties understanding the code and its explanation have a look at our tutorial on How to Control Servo Motors with Arduino, which provides more details about servos.

#include "Servo.h"

#define IR_RECEIVE_PIN 8

const int ldrPin = A0;
const int servoPin = 9;

const int range = 780;
const int mid = 1600;

const int midValue = 500;
const int midRange = 10;

int ldrValue = 0;
int angle = 0;

Servo servo;

void rotate(int angle) {
  angle = map(angle, 0, 180, mid - range, mid + range);
  servo.writeMicroseconds(angle);
}

void setup() {
  Serial.begin(9600);
  pinMode(ldrPin, INPUT);
  servo.attach(servoPin);
}

void loop() {
  ldrValue = analogRead(ldrPin);

  int angle = (ldrValue - midValue) * 0.8;
  angle = constrain(angle, -90, +90);

  if (angle > midRange) {
    rotate(90 + angle);
  } else if (angle < -midRange) {
    rotate(90 + angle);
  } else {
    rotate(90);
  }

  delay(100);
}

The code has some additional constants such as range and mid that are used in the rotate() function, which rotates the servo to a given angle. Apart from the constants and functions related to the servo the remainder of the code is very similar to the code you have seen above.

The interesting things are all happing in the loop() function. There we convert the ldrValue to an angle by subtracting the midValue from ldrValue and multiplying by a factor of 0.8.

int angle = (ldrValue - midValue) * 0.8;

The factor of 0.8 is something you need to adjust based on the dimensions/physics of your system; specifically how far apart the LDRs are. As before, there is no guaranty that the angle value will be in the range from -90 to +90 degrees. We therefore constrain it:

 angle = constrain(angle, -90, +90);

Finally, we decide if we turn the servo left or right depending on the angle we detected. If the angle change is small (-midRange+midRange), we keep the servo at 90 degrees.

  if (angle > midRange) {
    rotate(90 + angle);
  } else if (angle < -midRange) {
    rotate(90 + angle);
  } else {
    rotate(90);
  }

With this circuit and code you can now control the angle of the servo depending on the light direction. You could use this to build a car that steers toward light. With two of these light direction detectors in a perpendicular orientation you could build a solar panel that follows the movements of the sun for optimal performance.

However, for light following applications you should prefer a continuous servo over the positional servo used here. This would allow you to rotate the servo in negative or positive direction until the midpoint is reached. For more details read our tutorial on the differences of Positional versus Continuous Servos.

Some other applications of light detectors are described below.

Applications

Home Automation

By using a light detector, you can automate various tasks in your home. For instance, you can program your Arduino to turn on the lights when it detects darkness and turn them off when it detects light. This can help save energy and provide convenience.

Security Systems

Light detectors can be used in security systems to trigger alarms or activate cameras when there is a sudden change in light intensity. This can help detect intruders or unusual activities in a specific area.

Plant Monitoring

Light is crucial for the growth of plants. By using a light detector, you can monitor the amount of light received by your plants and ensure they are getting the optimal conditions for growth. This can be particularly useful for indoor gardening or greenhouse setups.

Weather Stations

Light detectors can also be used in weather stations to measure the intensity of sunlight. This information can be used to analyze weather patterns, calculate solar radiation, or even predict weather conditions.

Art Installations

Light detectors can be incorporated into interactive art installations to create dynamic and responsive lighting effects. By detecting changes in ambient light, the Arduino can control the brightness, color, or pattern of the lights, creating an immersive experience for viewers.

These are just a few examples of the many applications of light detection using an Arduino. The versatility of Arduino and its compatibility with various sensors make it an excellent choice for implementing light-based projects.

Summary

In this tutorial, we have learned how to detect light using an Arduino and explored various applications of light sensors in maker projects. By using a photoresistor, we can easily measure the intensity of light in our surroundings.

We started by understanding the basic concept of a photoresistor and how it works. A photoresistor is a type of resistor that changes its resistance based on the amount of light falling on it. This property makes it an ideal component for light detection.

We then went on to build a simple light detector using an Arduino and a photoresistor. By connecting the photoresistor to an analog pin of the Arduino, we were able to read the resistance value and convert it into a corresponding light intensity value. This allowed us to create a basic light sensing system.

Next, we explored more advanced applications of light sensors. We built an automatic light switch that turns on or off a light source based on the ambient light conditions. This can be useful in saving energy by automatically controlling the lighting in a room.

We also built a regulated light system, where the intensity of a light source is adjusted based on the surrounding light conditions. This can be helpful in maintaining a consistent level of lighting in a specific area.

Lastly, we built a light-controlled servo system. By using the light intensity values from the photoresistor, we were able to control the movement of a servo motor. This opens up possibilities for creating interactive projects that respond to changes in light.

We hope this tutorial has provided you with a solid foundation for working with light sensors and Arduino. Feel free to explore the various applications mentioned here and let your creativity shine!

Frequently Asked Questions

Here are some frequently asked questions about detecting light using an Arduino:

Can I use a different microcontroller board instead of an Arduino?

Yes, you can use other microcontroller boards such as ESP32, Raspberry Pi, or STM32 for detecting light. The principles and techniques discussed in this blog post can be applied to any microcontroller board that supports analog input.

How do I determine the polarity of a photoresistor?

Photoresistors, also known as light-dependent resistors (LDRs), are non-polarized components. This means that they do not have a specific positive or negative terminal. You can connect them in any orientation without worrying about the polarity.

What is the resistance range of a photoresistor?

The resistance of a photoresistor varies with the intensity of light falling on it. In bright light, the resistance decreases, and in darkness, the resistance increases. The resistance range typically varies from a few hundred ohms in bright light to several megaohms in darkness. It is important to choose the appropriate resistor values in your circuit to ensure accurate light detection.

How can I calibrate the light detection circuit?

To calibrate the light detection circuit, you can adjust the values of the resistors in the voltage divider circuit connected to the photoresistor. By measuring the output voltage of the voltage divider at different light levels, you can determine the threshold values for light detection. This calibration process ensures that the circuit responds accurately to the desired light levels.

Can I use multiple photoresistors in a single project?

Yes, you can use multiple photoresistors in a single project. By connecting multiple photoresistors to different analog input pins of the Arduino or other microcontrollers, you can monitor light levels from various locations or directions. This can be useful in applications such as security systems, automated lighting, or environmental monitoring.

Can I use a light sensor module instead of a photoresistor?

Yes, you can use light sensor modules that come with built-in photoresistors or other light sensors. These modules often provide additional features such as adjustable sensitivity or digital output signals. When using a light sensor module, refer to its datasheet or documentation to understand its pinout and operating characteristics.

How are photoresistors different from PIR motion sensors?

Photoresistors and passive infrared (PIR) motion sensors are both used for detecting environmental changes, but they work on different principles. The former detect changes in light intensity, while the latter detect changes in infrared radiation caused by moving objects. Photoresistors are suitable for applications where light levels need to be monitored, while PIR motion sensors are ideal for detecting human or animal motion.

If you have any more questions or need further assistance, please feel free to ask in the comments section below.

Links

Below some useful links for further reading