Table of contents
1 Practical Photoresistance
1.1 What is a LDR / photoresistor?
1.2 You will learn how to …
2 Example circuits
2.1 Analog darkness mitigator
2.1.1 BOM
2.1.2 MAKE it!
2.2 Digital darkness mitigator
2.2.1 BOM
2.2.2 MAKE it!
LDRs rely upon photoconductivity, which for the purposes of this article can be summarized as having low resistance through the photoresistor when exposed to light, and high resistance when not exposed to light.
Between these two points is a lot of room for sensing the actual light levels that the LDR is exposed to; the type I’m using has only ~500kOhm in absolute darkness, and ~100ohm in strong noon sunlight. The fact that the temperature is affected by the LDR’s temperature makes the photoresistor unsuitable for high-precision light measurements, but it leaves many applications open.
Additionally, note that there’s a ~10ms response time to light changes, which may or may not be a factor in your application.
Another thing worth noting is that LDRs have such a high resistance in darker settings that you can’t reliably measure them while holding them with your hands. Your body’s resistance is fairly high at ~2-3 megaohms, and LDRs will parallel with your body if you hold one multimeter probe in one hand, touch one lead, while also having skin contact with the other probe, on the other lead. Use flexible pincer clips if the LDR slips away from you.
In the example circuits section, I’ll demonstrate the concepts used to drive streetlights (before the advent of centralized control), and many other systems which must sense the ambient light levels. It’s relevant to note in advance that an LDR is, by itself, not enough to reliably affect a complex circuit in a useful way. A voltage divider is often used, giving you a better granularity with regard to driving FETs and reading light levels (with e.g. an Arduino). A voltage divider consists of two resistors in series. Between these two, we measure a useful voltage. Try measuring the point between R1 and R2, where R1 is the first resistor in the series and R2 the second. Consider the following formula:
1 2 3 4 5 |
V_out = V_in * ( R2 / ( R1 + R2 ) ) For example: V_out = 5 * ( 330 / ( 1000 + 330 ) ) = 1.24V |
For the following diagram:
Practically speaking, if you input 5V to this voltage divider, and measure the potential from the point between R1 (1000ohm) and R2 (330ohm), to ground, you’ll read ~1.24V on your multimeter.
Much more interestingly, if R1 has a variable resistance as LDRs do under varying lighting conditions, and R2 is a 10 kiloohm potentiometer, you can finely tune the output voltage from the voltage divider to be well within the tolerances of FET gates/BJT bases or microcontroller ADCs, and yet maintain a high enough resolution to determine how much light strikes the LDR.
This is extremely useful for example when using the ESP8266 ADC, which can always safely tolerate 1V. Arduinos based on atmega328p tolerate no more than 5V, while those based on atmega168 tolerate no more than 3.3V. If you have a 12V signal, and want to read it with your Arduino, use a voltage divider.
Note that voltage dividers aren’t suitable for being loaded, only for control signals and measurements. A significant load will affect the divider and heat the resistors; use a voltage regulator or buck converter to step down in a way suitable for supporting loads at a stable voltage.
An easy voltage divider circuit is used in the infrared Raspberry Pi NoIR camera module, which turns on its infrared LEDs only when the ambient light levels require it. You’ll need a P-channel MOSFET or PNP transistor to utilize this in as simple a manner as possible.
For this practical purpose, you only need to know that a P-channel MOSFET or PNP transistor is turned _OFF_ when its gate or base is HIGH. What constitutes HIGH is variable, but on Arduinos it’s usually adequate to apply 5V or 3V to open the circuit and disrupt the flow of electrons. When brought LOW (0V), the P-channel MOSFET or PNP transistor will close the circuit, and the electrons can flow. It’s often necessary to use pull-up resistors to keep this type of transistor from conducting all the time!
In section 2, you’ll learn how to create analog LDR circuits as well as digital LDR circuits. The analog LDR circuit in 2.1 can be created as a plug-in hardware module to modify the behavior of existing hardware (it’ll be quite small), and can easily drive a relay or similar, while the circuit and code in 2.2 can be used as a template for any kind of circuit you can implement with an Arduino. The latter does only read an LDR through a voltage divider, but can easily be expanded. A popular build is the light-logger used before deploying solar panels, where you build a battery-backed Arduino with microSD, set to log light levels at specific angles, in specific locations.
For the circuits below, we’ll be using a PNP transistor, though a P-channel MOSFET would be a neater choice. Be mindful that the key practical difference is that the PNP responds to current going to its base, whereas the MOSFET consumes much less current, and switches depend on the voltage supplied, not the current.
That means there must always be a current-limiting resistor in series with a PNP base, or it’ll burn. In the case of an Arduino, with a 5V digital pin output, a 220ohm resistor is adequate ( 5V / 220R = ~23mA ), or, if you want to be totally correct, a 270ohm resistor ( 5V/270R = ~18mA ). As a rule, you can only draw ~20mA of current from an Arduino (both atmega328p and atmega168) pin, and while drawing ~40mA is not uncommon, it’s going to reduce the lifespan of your board significantly. Don’t do it.
These technicalities aside, LDR circuits can be deployed cheaply and easily in almost any circuit to control its behavior. The novelty LED lamp (just 3 LR44 1.5V batteries, a LED, a resistor and a switch) below was modified for the purposes of extending battery life. Light sensitivity can be tuned on the potentiometer, and the LDR is facing a window.
It’s also possible to use LDRs to build simple guidance systems for solar panels, to ensure a nearly perfect perpendicularity to the sun (the angle at which the panels yield the highest possible power on their output is 90°).
To achieve this, two or four LDRs may be used in a cross-configuration for a single-axis and dual-axis solar tracking robots.
This does require a microcontroller with an ADC, but is a sure way to capture all the available power from the panels, at the cost of only a few stepper motor micro-steps every now and then. The rest of the time, the microcontroller should be sleeping. Have a look at the A4988 motor driver for perfect motor control in such semi-complex circuits.
When the LDR is struck by light in the range from ~400nm to ~600nm, it’ll drastically reduce its resistance, whereas in the absence of light, its resistance will become so high that we can call it effectively non-conductive. It still conducts, of course, but e.g. 5V / 1000000R = 1uA, 1 microamp at a resistance of 1 megaohm. That’s so low a current that it would require an operational amplifier to do anything useful. If you’re unfamiliar with Ohm’s law, please read this guide.
Observe the difference in resistance under an LED penlight (not a full-spectrum light source), and subsequently shaded.
You’ll need these parts:
Wire everything up as shown in the diagram.
Four ROHM SLR343BC4TT32 3mm LEDs are controlled using an LDR and potentiometer, which in turn switch a BC557 PNP transistor. Since it doesn’t rely upon any digital electronics, it’s small and easy to implement, and you can install such circuitry easily, e.g. in a lamp in your garden or garage.
If you’re electrically curious about the 220ohm resistor in series with the BC557 base, it’s only in place to prevent you from baking your transistor, should you experience super-low resistance on the LDR while having the 10kiloohm potentiometer turned way down to nearly 0 ohms resistance. It can be omitted. When the head of the LDR is exposed to normal lighting levels, the LEDs will turn on.
Once the circuit is wired onto your breadboard, tune the 10kiloohm potentiometer to your desired sensitivity. Expose the LDR to various lighting levels until you’re happy.
Pay careful attention to where the load is. On an NPN transistor, the load is usually in series with the collector, and on an N-channel MOSFET, the load is usually in series with the drain. The PNP and P-channel MOSFET are of opposite polarities to these, and in the circuit diagram above, the load is on the emitter side, with the collector going directly to 5V.
It’s this type of circuit that was used to drive streetlights in the olden days, and it’s never quite gone out of style. That’s to say that you can use it as a relay driver circuit, and, in place of the LEDs, instead have a 5V relay that switches on a much heavier load, such as an AC light, or a bug-zapper, for that matter. It’s in the summertime that the flying, biting things come for you. Give them something else to come for. This is a perfect “Hello World” circuit for mosquitoes!
While the above circuit is certainly useful, you should also learn to read light levels with an ADC (analog-to-digital converter). In this case, let’s use the Arduino Uno or Nano.
You’ll need these parts:
Arduino Uno or Arduino Nano | https://store.arduino.cc/arduino-uno-rev3 |
10kiloohm potentiometer | https://www.newark.com/bourns/3296w-1-103lf/pot-trimmer-10k-25turn-10/dp/39K2049 |
LDR | https://www.newark.com/lprs/n5ac501085/ldr-5mohm-50mw-ng-series/dp/14J5050 |
Breadboard | https://www.newark.com/multicomp/mcbb830/breadboard-solderless-abs/dp/99W1760 |
Jumper wires | https://www.newark.com/adafruit/758/kit-contents/dp/88W2570 |
Wire up your Arduino Uno or Nano with a 10kiloohm potentiometer and LDR forming a voltage divider, in the following way:
Then, download the latest Arduino IDE here and follow the relevant instructions for your system found here, under the section Install the Arduino Desktop IDE.
The Arduino IDE is outside the scope of this tutorial, and you’ll only need the keyboard combos CTRL+U to upload, and CTRL+SHIFT+M to view serial output.
Paste the following lines into a new sketch, and save it as LDR_analogRead.ino. Then, upload the sketch, and open your serial monitor.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
void setup( void ) { Serial.begin( 9600 ) ; // Declaring this an input is // only to keep // track of pin usage in setup(). pinMode( A0, INPUT ) ; } void loop( void ) { delay( 100 ) ; //Serial.println( F( "[!] Looping" ) ) ; } void serialEvent( void ) { Serial.print( F( "[!] analogRead( A0 ) => " ) ) ; Serial.println( analogRead( A0 ) ) ; // Flush the Serial buffer ... while ( Serial.available() ) Serial.read() ; } |
In the serial monitor, send some random character, and for each “send,” you’ll see output similar to this:
1 2 3 4 5 6 7 |
[!] analogRead( A0 ) => 607 [!] analogRead( A0 ) => 610 [!] analogRead( A0 ) => 604 [!] analogRead( A0 ) => 610 [!] analogRead( A0 ) => 597 |
We don’t use the loop() function much here, since the serialEvent() function is much more efficient. There will only be output on the serial port if you connect and press a key (any key). This prevents debug output from bogging down your Arduino, as no data will ever go out over the serial port unless you first send something to your Arduino.
In this little program, the Arduino will read the voltage on the A0 pin, with a resolution of 10 bits, yielding 1024 ( 2**10 ) possible values from 0 to 1023. Each of these values corresponds to a voltage from 0 to 5 volts. That’s ~0.0048V per step, so a reading of 512 would equal ~2.46V.
Now that you’ve learned how to use LDRs, as well as a bit about transistors and Arduino, try to add light-sensing functionality to an existing circuit, or build your own from scratch.
Go forth and harness the power of light and darkness!