Overview
The KY-018 is the classic first light sensor: a photoresistor (LDR) whose resistance falls as light hits it, paired with a fixed resistor to form a voltage divider. The S pin outputs the divider's midpoint voltage, so as the room gets brighter or darker, the analog value your board reads slides smoothly up and down. No protocol, no library, no configuration — one analogRead() and you are measuring light.
The LDR responds like a human-friendly light meter: from over a megaohm in darkness down to a few kilohms in bright light, with most of the change happening in the everyday indoor range. That makes the KY-018 ideal for threshold decisions — is it day or night, did someone switch on the lamp, is the enclosure lid open, did a shadow pass over the desk — rather than laboratory lux measurements (for calibrated lux, the BH1750 digital sensor is the right tool).
It runs on 3.3V or 5V, and because the output can never exceed the supply, it wires directly to the ESP32 and Pico when powered at 3.3V. Arduino reads it directly at 5V, and the Raspberry Pi — which has no analog inputs — reads it through an ADS1115 ADC. Typical projects: automatic night lights, dusk-triggered blinds, light-following robots, laser tripwires, and plant-light monitors.
At a Glance
Specifications
| Parameter | Value |
| Sensing Element | CdS photoresistor (GL5528 class) |
| Circuit | LDR + fixed resistor voltage divider |
| Output | Analog voltage on S (0 to VCC) |
| Operating Voltage | 3.3V - 5V DC |
| Light Resistance | ~8 - 20 kΩ at 10 lux |
| Dark Resistance | > 1 MΩ |
| Response Behavior | Brighter light → higher S voltage on this board layout |
| Response Time | Tens of milliseconds (CdS cells are not instant) |
| Spectral Peak | ~540 nm (green — close to human eye response) |
| Board Format | KY-018 3-pin module, ~36 x 18 mm |
Pinout Diagram
Three pins, marked on the silkscreen: S is the analog signal, the middle pin is VCC, and the pin marked with a minus sign is GND. On this board the LDR sits between VCC and S with the fixed resistor from S to ground, so more light pulls S toward VCC — brighter room, bigger number. If you ever meet a KY-018 revision wired the other way (readings fall with light), nothing is wrong; just flip your comparison in code.
Wiring Guide
Arduino Wiring
| KY-018 Pin | Arduino Pin |
|---|---|
| S | A0 |
| VCC (middle) | 5V |
| - (GND) | GND |
ESP32 Wiring
Power from 3V3 so the S output tops out at 3.3V — inherently safe for the ESP32's ADC.
| KY-018 Pin | ESP32 Pin | Details |
|---|---|---|
| S | GPIO 34 | ADC1 channel, input-only pin |
| VCC (middle) | 3V3 | Do NOT use VIN/5V |
| - (GND) | GND |
Raspberry Pi Wiring
The Pi has no analog inputs, so an ADS1115 I2C ADC reads the S pin. Power everything from the 3.3V rail.
| Wire / Pin | Connects To | Details |
|---|---|---|
| KY-018 S | ADS1115 A0 | Analog channel 0 |
| KY-018 VCC | Pin 1 (3.3V) | Shared rail |
| KY-018 GND | Pin 6 (GND) | |
| ADS1115 VDD / GND | Pin 1 / Pin 6 | |
| ADS1115 SDA | Pin 3 (GPIO 2) | I2C data |
| ADS1115 SCL | Pin 5 (GPIO 3) | I2C clock |
Raspberry Pi Pico Wiring
| KY-018 Pin | Pico Pin | Details |
|---|---|---|
| S | GP26 (pin 31) | ADC0 input |
| VCC (middle) | 3V3(OUT) (pin 36) | Do NOT use VBUS (5V) |
| - (GND) | GND (pin 38) |
Code Examples
Each example prints the raw reading plus a simple day/dim/dark classification. The thresholds are starting points — tune them to the numbers you see in your own room.
Arduino
// KY-018 Photoresistor - Arduino Example
// S -> A0, VCC (middle) -> 5V, - -> GND
const int sensorPin = A0;
void setup() {
Serial.begin(9600);
}
void loop() {
int raw = analogRead(sensorPin); // 0-1023, higher = brighter
Serial.print("Light level: ");
Serial.print(raw);
if (raw > 700) {
Serial.println(" (bright)");
} else if (raw > 300) {
Serial.println(" (dim)");
} else {
Serial.println(" (dark)");
// Example action: digitalWrite(LED_BUILTIN, HIGH); // night light on
}
delay(500);
}
ESP32 (MicroPython)
# KY-018 Photoresistor - ESP32 MicroPython Example
# S -> GPIO 34, VCC (middle) -> 3V3, - -> GND
from machine import ADC, Pin
import time
adc = ADC(Pin(34))
adc.atten(ADC.ATTN_11DB) # full 0-3.3V range
while True:
raw = adc.read() # 0-4095, higher = brighter
if raw > 2800:
state = "bright"
elif raw > 1200:
state = "dim"
else:
state = "dark"
print("Light level: {} ({})".format(raw, state))
time.sleep(0.5)
Raspberry Pi (Python + ADS1115)
#!/usr/bin/env python3
# KY-018 Photoresistor - Raspberry Pi + ADS1115 Example
# S -> ADS1115 A0, SDA/SCL -> GPIO 2/3, VCC -> 3.3V
# Install: pip3 install adafruit-circuitpython-ads1x15
import time
import board
import busio
import adafruit_ads1x15.ads1115 as ADS
from adafruit_ads1x15.analog_in import AnalogIn
i2c = busio.I2C(board.SCL, board.SDA)
ads = ADS.ADS1115(i2c)
ads.gain = 1 # +/-4.096V range
channel = AnalogIn(ads, ADS.P0)
try:
while True:
volts = channel.voltage # 0-3.3V, higher = brighter
if volts > 2.3:
state = "bright"
elif volts > 1.0:
state = "dim"
else:
state = "dark"
print("Light level: {:.2f} V ({})".format(volts, state))
time.sleep(0.5)
except KeyboardInterrupt:
print("Stopped by user")
Raspberry Pi Pico (MicroPython)
# KY-018 Photoresistor - Pico MicroPython Example
# S -> GP26 (ADC0), VCC (middle) -> 3V3(OUT), - -> GND
from machine import ADC
import time
adc = ADC(26)
while True:
raw = adc.read_u16() # 0-65535, higher = brighter
if raw > 45000:
state = "bright"
elif raw > 20000:
state = "dim"
else:
state = "dark"
print("Light level: {} ({})".format(raw, state))
time.sleep(0.5)