To use an LDR (Light Dependent Resistor) in Arduino, you cannot wire it directly to an analog pin. Because the Arduino's ADC (Analog-to-Digital Converter) measures voltage, not resistance, you must wire the LDR in a voltage divider circuit with a fixed resistor to convert resistance changes into a 0-5V signal. For the most common hobbyist LDR (the GL5528), the correct fixed resistor to use is 10kΩ.
This guide provides the exact mathematical framework for sizing your divider, a complete wiring procedure, and production-ready C++ code that includes hysteresis to prevent the classic 'twilight flickering' failure mode.
The Core Decision: Sizing Your Voltage Divider Resistor
The most common mistake in LDR projects is grabbing a random 220Ω or 1kΩ resistor from a bin. This compresses your analog reading range into a useless 20-point window. To maximize the ADC resolution across your specific lighting environment, you must calculate the geometric mean of the LDR's dark and light resistance.
The formula for the ideal fixed resistor ($R_{fixed}$) is:
$R_{fixed} = \sqrt{R_{dark} \times R_{light}}$
| LDR Model | Dark Resistance (Approx) | Light Resistance (10 lux) | Calculated Ideal $R_{fixed}$ | Standard E12 Pick (Use This) |
|---|---|---|---|---|
| GL5516 | 500kΩ | 5kΩ | 50kΩ | 47kΩ |
| GL5528 (Most Common) | 1MΩ | 10kΩ | 100kΩ | 100kΩ (or 47kΩ for indoor use) |
| GL5537-1 | 2MΩ | 20kΩ | 200kΩ | 200kΩ |
| Advanced Photonix PDV | 10MΩ | 1kΩ | 100kΩ | 100kΩ |
Parts List and Pin Mapping
This build targets the Arduino Uno R3 (ATmega328P). The code relies on the 10-bit ADC resolution (0-1023). If you are using an ESP32 (12-bit, 0-4095) or Arduino Uno R4 Minima (14-bit, 0-16383), you must scale the threshold constants in the code block below.
Required Components
- Microcontroller: Arduino Uno R3 (or compatible ATmega328P clone)
- Sensor: GL5528 Photoresistor (5mm epoxy coated)
- Fixed Resistor: 10kΩ, 1/4W, 5% tolerance (Carbon or Metal Film)
- Output Indicator: 5mm Standard LED (any color)
- Current Limiting Resistor: 220Ω or 330Ω (for the LED)
- Hardware: Half-size breadboard, male-to-male jumper wires
Pin Mapping Table
| Component | Arduino Pin | Wire Color (Recommended) | Function |
|---|---|---|---|
| LDR + 10kΩ Junction | A0 | Yellow | Analog Input (Voltage Divider Output) |
| LDR Leg 1 | 5V | Red | VCC Supply |
| 10kΩ Resistor Leg 2 | GND | Black | Reference Ground |
| LED Anode (+) | D8 | Green | Digital Output (PWM capable if needed) |
| LED Cathode (-) | GND (via 220Ω) | Black | Current Return |
Step-by-Step Wiring Procedure
- Build the Voltage Divider: Insert the LDR into the breadboard. Connect one leg to the 5V rail. Insert the 10kΩ fixed resistor so one leg shares the same row as the LDR's other leg. Connect the free leg of the 10kΩ resistor to the GND rail.
- Route the Analog Signal: Run a jumper wire from the shared junction row (where the LDR and 10kΩ resistor meet) directly to Arduino pin A0.
- Wire the Output LED: Place the LED on the board. Connect the anode (long leg) to pin D8 through the 220Ω current-limiting resistor. Connect the cathode (short leg) to GND.
- Physical Placement Check: Bend the LDR slightly away from the LED. If the LED's output shines directly onto the LDR's epoxy face, you will create an optical feedback loop that causes high-frequency oscillation when the room is dim.
Complete Arduino Code with Hysteresis
A naive if (sensorValue < 500) statement will cause your output to chatter rapidly at twilight as ambient light bounces across the threshold due to micro-fluctuations (clouds, passing cars, AC ripple). This code implements a hysteresis band to ensure clean, decisive switching.
/*
* LDR Twilight Switch with Hysteresis
* Target Board: Arduino Uno R3 (ATmega328P, 10-bit ADC)
* Author: ElectricalFlux
*/
// --- Pin Definitions ---
#define LDR_PIN A0
#define LED_PIN 8
// --- Threshold Configuration ---
// Adjust these based on your Serial Monitor readings in your specific room
#define DARK_THRESHOLD 400 // Value where lights turn ON (getting dark)
#define LIGHT_THRESHOLD 550 // Value where lights turn OFF (getting bright)
// Hysteresis gap is 150 units (550 - 400), preventing twilight chatter
// --- State Variables ---
bool isDark = false;
void setup() {
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW); // Ensure LED starts off
Serial.begin(115200);
Serial.println(F("LDR Hysteresis Controller Initialized."));
Serial.println(F("Format: [Raw_ADC] | [State]"));
}
void loop() {
// Read the analog pin (0-1023 for 10-bit ADC)
int rawLightLevel = analogRead(LDR_PIN);
// Error handling: Check for disconnected/floating pin
// A truly floating pin on ATmega328P often reads wildly, but if
// wired directly to 5V or GND without the divider, it pins at 1023/0.
// Hysteresis Decision Logic
if (!isDark && rawLightLevel < DARK_THRESHOLD) {
// It was bright, but now it's dark enough to trigger
isDark = true;
digitalWrite(LED_PIN, HIGH);
}
else if (isDark && rawLightLevel > LIGHT_THRESHOLD) {
// It was dark, but now it's bright enough to reset
isDark = false;
digitalWrite(LED_PIN, LOW);
}
// Serial Telemetry for Debugging
Serial.print(rawLightLevel);
Serial.print(" | ");
Serial.println(isDark ? "DARK (LED ON)" : "LIGHT (LED OFF)");
// Sample rate limiting (adjust based on LDR response time, typically 20-50ms)
delay(100);
}
Debugging: First 3 Things to Check When It Fails
When your serial monitor output doesn't match the physical lighting in the room, work through this ranked diagnostic path.
1. Serial output reads: 1023 continuously
- Cause A (Most Likely): The LDR is wired to 5V, but the 10kΩ fixed resistor to GND is missing, loose, or broken. The A0 pin is being pulled directly to 5V.
- Cause B: You swapped the LDR and the fixed resistor. If the LDR is on the bottom (connected to GND) and the fixed resistor is on top (connected to 5V), the voltage at A0 will approach 5V in the dark (when LDR resistance spikes to 1MΩ), maxing out the ADC.
- Fix: Verify the 10kΩ resistor has a solid physical connection to the breadboard GND rail. Ensure LDR is on the 5V side.
2. Serial output reads: 0 continuously
- Cause A (Most Likely): The voltage divider is inverted, and the room is brightly lit. If the LDR is on the bottom (GND side) and the fixed resistor is on top (5V side), a low LDR resistance in bright light will pull A0 directly to GND.
- Cause B: A short circuit between the A0 jumper wire and the breadboard GND rail.
- Fix: Swap the physical positions of the LDR and the 10kΩ resistor so the LDR connects to 5V and the fixed resistor connects to GND.
3. Serial output reads: nan or wild jumps (e.g., 412, 899, 3)
- Cause A (Most Likely): The A0 pin is floating. The junction wire between the LDR and the fixed resistor is not making contact with the analog pin.
- Cause B: High-frequency noise from nearby AC mains wiring or a switching power supply is coupling into the high-impedance analog trace.
- Fix: Reseat the jumper wire in A0. If the jitter persists, solder or breadboard a 0.1µF ceramic capacitor directly between the A0 pin and GND. This creates a low-pass RC filter that smooths out AC ripple and breadboard contact noise.
Extending and Simplifying the Build
Depending on your end goal, you may not need the Arduino's ADC at all, or you may need to scale this up to handle high-voltage loads.
How to Simplify: Drop the Arduino Entirely
If your only goal is to turn on a 12V LED strip when the sun sets, using an Arduino is overkill. Instead, purchase an LM393 Light Sensor Module (typically $2-$4). These modules feature the LDR and a comparator IC on a single PCB. You adjust a physical trimpot with a small screwdriver to set the exact twilight threshold. The module outputs a clean digital HIGH or LOW signal that can directly drive a logic-level MOSFET (like an IRLZ44N) to switch 12V loads without writing a single line of C++.
How to Extend: Switching Mains AC Loads
The Arduino GPIO pin (D8) can only source 20mA safely. To switch a 120V/240V AC porch light based on your LDR readings:
- Do not wire a mechanical relay directly to D8; the inductive kickback will fry the ATmega328P.
- Use an opto-isolated relay module (e.g., Songle SRD-05VDC-SL-C). Wire the module's VCC to the Arduino 5V, GND to GND, and the IN pin to D8.
- For silent, solid-state switching with zero EMI, replace the mechanical relay with a random-fire SSR (Solid State Relay) like the Fotek SSR-25DA, driven via a small NPN transistor (2N2222) from the Arduino pin.
For deeper reading on ADC behavior and voltage divider theory, refer to the official Arduino analogRead() documentation and the SparkFun Voltage Divider tutorial.






