A Light Activated Switch

Up until now we mostly talked about digital signals and digital values. Digital signals are series of discrete values that vary at regular intervals of time in discrete steps. All digital signals can be represented as sequences of integers. There are a finite number of values each digital quantity can have, including when it is represented by a floating-point number. By contrast, analog signals are values that continuously vary with time. Analog signals can be represented using real numbers. Real-world measurements of voltage, current, or speed are analog signals. Since computers and processors can only work with digital values and signals, any real-world analog quantity or signal must be converted to its digital counterpart in order to be processed. Fortunately, Arduino boards have analog input ports that convert voltages into digital values.

The featured image at the beginning of this blog post is titled “Sodium Light Lit Tree at Rogerthorpe Manor” by Andy Beecroft, CC BY-SA 2.0 and was downloaded as-is from the Wikimedia Commons.

Analog to Digital Conversion

The micro-controller on the Arduino board has an analog to digital converter (ADC) that converts the voltage found at any one of the analog input pins to an integer value between 0 and 1023. The analog to digital converter splits the input voltage range into 1024 equal steps. By default, the Arduino’s analog input voltage range is between 0 volt and the Arduino’s supply voltage (VCC), approximately 5 volts for the Arduino Uno. Each step in the range spans VCC/1024 volts or approximately 0.00488 volts (4.88 mV) for the Arduino Uno. So, if the voltage at the analog input pin is between 0 V and 4.88 mV then the digital value is 0; if it is between 4.88 mV and 9.76 mV then the digital value is one; and so on until the input value is between VCC − 4.88 mV and VCC producing a discrete value of 1023. Hence, the discrete value returned by the ADC is

n = V∙1024 / VCC

where n is the integer discrete value returned by the ADC and V is the voltage at the analog input pin of the Arduino.

Once the input voltage has been converted to a discrete digital step value, we may want to convert that digital number to its corresponding digital floating-point voltage value to be able to use it in equations or formulas. We could convert the digital value to two floating-point numbers corresponding to the voltage range covered by the digital value returned by the ADC. A value of n at the input pin corresponds to analog voltages between n∙VCC/1024 and (n + 1)∙VCC/1024. For instance, if the value sampled is 100, the corresponding voltages would be between 100∙VCC/1024 and 101∙VCC/1024, or approximately between 0.488 V and 0.493 V on the Arduino Uno. However, using two values is not very practical. Instead, we can use the average value between the two extremes and state that there is a possible error of up to half the value of a step. Using our example, the average voltage for a sample value of 100 would be

(100∙VCC/1024 + 101∙VCC/1024) / 2 with a maximum error of ±0.5∙VCC/1024

On the Arduino Uno, by default, the voltage corresponding to a value of 100 is thus approximately 0.491 V ± 2.44 mV. More generally, the equation to convert the value returned by the ADC to a floating-point voltage value is

Voltage = (n + 0.5)∙VCC/1024 ± 0.5∙VCC/1024

Where n is the digital value returned by the ADC. The reason we used VCC in the equation is that the actual power supply voltage for the Arduino Uno is never quite 5 volts and the value measured and returned by the ADC is relative to VCC. In applications requiring more accuracy, the actual supply voltage would have to be measured, or better yet, a precise and stable voltage reference would have to be supplied. The conversion of input voltages into digital values by the ADC and the conversion from the digital value to its floating-point value can be visualized in the following graph.

In the graph, VCC has been changed to Vref, the reference voltage of the ADC which is VCC by default on the Arduino Uno.

An Automatic Light Switch

Light intensity is an analog real-world value that can be measured and converted to a voltage. Several light sensitive devices can be used to do so, and one such device is the photo-resistor. In the next few sections, we will see how a photo-resistor can be used to convert light intensity into a measurable voltage, how to read from an analog input pin on the Arduino Uno, and how to turn a light appliance on when surrounding light intensity drops below a predetermined threshold. But first, what is a photo-resistor?

The Photo-Resistor

A photo-resistor, or LDR for Light Dependent Resistor, is a two-terminal passive device whose resistance varies according to the amount of visible light reaching it. The resistance across its leads decreases as more light reaches its detection surface. As for any electronic device, the photo-resistor has specifications detailing its characteristics. Some of the main characteristics are

  • Light resistance at 10 lux. This is the resistance in ohms across the device’s leads when 10 lux of light illuminates the device. The lux (symbol: lx) is a measure of illuminance, the amount of light that hits or goes through a surface. 10 lx approximately corresponds to the amount of light produced by a 60-watt incandescent light bulb reaching a one square meter surface 2.5 meters away from the light bulb.
  • Gamma (γ). This is the relationship between how the resistance changes with respect to light intensity changes. The value corresponds to the difference of the logarithm of the resistance divided by the difference of the logarithm of illuminance. γ = (log R1 − log R2) / (log I2 − log I1), where R1 is the resistance when illuminance is I1 and R2 is the resistance when illuminance is I2. Note that the order of resistance values is reversed from the order of illuminance values in the equation.
  • Dark resistance at 0 lux. This is the resistance in ohms across the device leads when no light reaches the device.
  • Power dissipation (at 25 °C). The maximum power the device can dissipate at a temperature of 25 °C.
  • Max voltage (at 25 °C). The maximum voltage the device can withstand between its leads at a temperature of 25 °C.

The symbol for a photo-resistor is shown above on the right. Also pictured on the left, photo-resistor GL5528, which has the following electrical characteristics:

  • Light resistance at 10 lux: 8 KΩ to 20 KΩ
  • Gamma (γ): 0.7
  • Dark resistance at 0 lux: 1.0 MΩ (minimum)
  • Power dissipation (at 25 °C): 100 mW
  • Max voltage (at 25 °C): 150 

Computing Resistance and Illuminance

There are two characteristics of the photo-resistor that can help us determine its resistance for a given illuminance: the light resistance at 10 lux, R10lx, and the gamma, γ. Let’s take the gamma equation stated earlier in the photo-resistor characteristics section:

γ = (log R1 − log R2) / (log I2 − log I1)

Substituting R1 with the light resistance at 10 lux, R10lx, and I1 with 10 lx we get the following equation:

γ = (log R10lx − log R) / (log I − log 10)

where R is the unknown resistance value for a given illuminance I. Using the logarithm laws, we can solve the equation for R, which gives:

R = (10γ∙R10lxγ) / Iγ

Similarly, we can solve the same equation for I, yielding:

I = (10∙R10lx1/γ) / R1/γ

The light resistance at 10 lux for photo-resistor GL5528 is between 8 KΩ and 20 KΩ according to its electrical specification. We will use an average light resistance at 10 lux of 14 KΩ. The gamma for photo-resistor GL5528 is 0.7 for illuminances between 10 lx and 100 lx. Substituting the light resistance value at 10 lux, the gamma and an illuminance of 10 lx in the above equations we get:

R = (100.7∙14,0000.7) / I0.7  or
R ≈ 70,000 / I0.7

and

I = (10∙140001/0.7) / R1/0.7  or
I ≈ 8,400,000 / R1.43

Converting Light Intensity to Voltage

We will use a voltage divider, as described in post “A Better Transistor Switch Circuit,” as a mean to convert photo-resistor electrical resistance into voltage. The following circuit diagram depicts the voltage divider circuit and its connection to the Arduino micro-controller.

We have to choose a resistance, R1, which limits enough current so that the phot-resistor does not overheat and so that the voltage at the analog input is close to the midpoint of the full voltage range. According to the GL5528 photo-resistor characteristics, the maximum power that the photo-resistor can dissipate is 100 mW at 25 °C. As we have previously seen in the Arduino’s Blink post, power dissipated through both the resistance and the photo-resistor is obtained by multiplying voltage and current together,

P = V∙I

and since I = V/R according to Ohm’s law, replacing I in the previous equation and R with the sum of both resistors, we get

P = V2 / (R1 + R2)

We know that the voltage, VCC, is approximately 5 volts and that P must be smaller than 0.1W. Hence, the following must hold:

V2 / (R1 + R2) < 0.1 W,
25 V2 / 0.1 W < R1 + R2

The sum of the resistance of both resistors must be larger than 250 Ω. Also, since we want to activate the light when the light intensity is below 10 Lux, or when the photo-resistor’s resistance is at or below 14 KΩ. Choosing resistor value of 15 KΩ for R1 satisfies both conditions. If R1 is 15 KΩ, the sum of both resistances is certainly larger than 250 Ω. Moreover, the voltage at the analog input pin will be approximately 2.41 V since

I = V / (R1 + R2) = 5 V / (15 KΩ + 14 KΩ) ≈ 0.172 mA

and

VR2 = R2∙I = 14K ∙ 0.172 mA ≈ 2.41V

The complete circuit becomes

We have the light detection part of the circuit. To activate the light, we will build on the circuit developed as part of the “A Better Transistor Switch Circuit” post and replace the push button switch with a GL5528 photo-resistor and connect the photo-resistor to an analog input pin instead of a digital input pin. The complete circuit is as follows:

Breadboarding

The following picture depicts how to connect the different parts using a solderless breadboard, jumper wires, a transistor, a diode, a relay, a photo-resistor, a 15K resistor, a 1.8K resistor and a 3.3K resistor. Connections to the household appliance are not shown.

The Light Activated Switch Program

We want the circuit to switch the lights on at dawn when it becomes dark and to switch the lights off in the morning when it is bright again. So, we want to switch the lights on when the illuminance at the photo-resistor falls below a preset value. Switching the lights on increases light intensity and turning them off when illuminance is above the same preset value as when we turned them on will cause oscillations, turning the lights repeatedly on and off. To prevent these unwanted oscillations, we need to implement hysteresis. Hysteresis is the dependence a system’s state has on its previous states; in our case, lights are to switch on when illuminance falls below a preset value only if their current state is off; and to switch them off if the illuminance is much higher than what it was when the lights were turned on only if their current state is on. Hysteresis will prevent oscillations if the illuminance at which the lights are to turn off is higher than the sum of the illuminance at which the lights are to turn on and the additional illuminance introduced by switching the lights on.

For example, let’s say a light appliance is to turn on when the light intensity caused by environmental lighting makes the illuminance at the photo-resistor fall below 10 lx. The light appliance being turned on causes an additional illuminance of 60 lx that can be measured at the photo-resistor. At this point, the illuminance at the photo-resistor is 70 lx. Turning the light appliance off only when the illuminance at the photo-resistor reaches 10 lx more, for a total of 80 lx, allows the lights to be turned off when the illuminance at the photo-resistor caused by environmental lighting is 20 lx, thus preventing any unwanted oscillations and turning the light appliance off at an appropriate light intensity.

In other words, the program must

  • Turn the light appliance on, if it was previously off, when the illumination falls below 10 lx.
  • Turn the light appliance off, if it was previously on, when the illumination rises above 80 lx.

We have already determined, from the photo-resistor specification that the device’s resistance at 10 lx is 14 KΩ on average. Using the equations developed earlier, the photo-resistance at 80 lx is 70,000 / 800.7 Ω, or approximately 3.3 KΩ. The voltage, V10, at the photo-resistor at 10 lx is

V10 = (5 V ∙ 14 KΩ) / (15 KΩ + 14 KΩ) ≈ 2.41V.

The voltage, V80, at the photo-resistor at 80 lx is

V80 = (5 V ∙ 3.3 KΩ) / (15 KΩ + 3.3 KΩ) ≈ 0.902 V.

As seen earlier, the integer value, n, read from the ADC is n = V∙1024 / VCC, where V is the voltage at the analog input. Thus, the integer value read from the ADC when the illuminance at the photo-resistor is 10 lx, and the voltage is 2.41 V, is 493. Similarly, the integer value read from the ADC when the illuminance at the photo-resistor is 80 lx, and the voltage is 0.902 V, is 184. Because the voltage varies inversely to luminance, the following will be implemented:

  • Turn the light appliance on, if it was previously off and the analog input reads greater than 493.
  • Turn the light appliance off, if it was previously on and the analog input reads less than 184.

The following Arduino program completes the post. Cut and paste the code in your Arduino IDE and download it to complete the project. It will turn the light appliance on when it gets dark and off again in the morning.

/*
  Light Dependent Resistor (LDR) Sensor Analog Reader Sketch
  Program that reads light intensity as an integer value from
  a GL5528 sensor, turns on a light appliance when the
  illuminance is smaller than 10 lx, if the light appliance
  is already off, and turns the light appliance off when the
  illuminance is greater than 80 lx, if the light appliance
  is already on. The program is associated with the "A Light-
  Activated Switch" blog post on https://lagacemichel.com
  MIT License
  Copyright (c) 2020, Michel Lagace
*/

// LDR (photo-resistor) analog input port and light appliance
// digital output port
#define LDR_PORT A0
#define LIGHT_SWITCH 11

// LDR voltage at which to turn the lights on and off
const int V10 = 493; // Analog value when LDR is 10 lx
const int V80 = 184; // Analog value when LDR is 80 lx
const int WaitInterval = 1000; // Interval between each loop

// Light appliance state
static bool LightOn = false;

// Setup the board.
void setup() {
  pinMode(LIGHT_SWITCH,OUTPUT);
  LightOn = false;
  digitalWrite(LIGHT_SWITCH,LightOn);
}

// This is the main loop. It acquires the luminance then turns
// on the light appliance if it is off and the illuminance
// falls below 10 lx (the voltage is above 2.41V), and turns
// off the light appliance if it is on and if the illuminance
// increases above 80 lx (the voltage is below 0.902 V).
void loop() {

  // Get sensor analog value from GL5528 sensor
  int LdrIntegerValue = analogRead(LDR_PORT);
 
  // If light is below 10 lux and currently off, turn on the light
  if (!LightOn && (LdrIntegerValue > V10)) {
    LightOn = true;
  }
  // If light is above 80 lx and currently on, turn off the light
  else if (LightOn && (LdrIntegerValue < V80)) {
    LightOn = false;
  }
  digitalWrite(LIGHT_SWITCH,LightOn);

  // Wait a while before repeating
  delay(WaitInterval);
}

Following the usual header, you will find definitions for LDR_PORT, the analog input port to read illuminance from, and LIGHT_SWITCH, the digital output port to control the light appliance. We then define three constants. First, V10, the voltage above which (or illuminance below which) to turn the light on, V80, the voltage below which (or illuminance above which) to turn the light off, and WaitInterval, a constant in milliseconds determining the time to wait between loops. Finally, the Boolean variable LightOn holds the on or off state of the light appliance, HIGH for turned on and LOW for turned off.

In the setup() function, we prepare the light appliance control by setting the LIGHT_SWITCH digital port to OUTPUT, and turn the light appliance off. In the loop() function, we read the analog input into variable LdrIntegerValue. If illuminance is below 10 lux, the voltage above V10, and currently off, turn the light appliance on. If illuminance is above 80 lx, the voltage below V80, and currently on, turn the light appliance off. Wait the prescribed amount of time and repeat.

WARNING:

The project in this post involves household mains high-voltages. Use caution whenever dealing with high-voltage wiring, including following directions carefully and following general safety practices. Safe assembly and operation of this project is the user’s responsibility. If unsure or if local laws prohibit the assembly of high-voltage circuits, get the help of a professional electrician. Do not make changes to the system while the device is plugged in.