Measuring light with an LDR sensor (ESP32)


An LDR sensor (Light Dependent Resistor) is a resistor whose resistance changes with the amount of light. When the light is stronger, the resistance of the LDR drops, and when the light is weaker, the resistance rises. Based on this change, we can measure the light intensity in a room.

Such sensors are used for automatic lighting, daylight monitoring, energy-saving systems, smart home automation and many IoT projects. In this exercise we use the ESP32 development board and an LDR sensor to read the light level.

ESP32 and LDR sensor

Practical setup

ESP32 connected to an LDR sensor while measuring light.

Wiring diagram of the ESP32 and the LDR sensor

Wiring diagram

Overview of all connections between the ESP32 board and the LDR sensor.

LDR sensor and resistor

Circuit elements

The LDR sensor, the resistor and the point where the analog signal is read.

Note:

On some ESP32 boards (e.g. ESP32-S3) the GPIO layout is different. In that case the analog input GPIO34 can be replaced with a suitable ADC pin (e.g. GPIO4), with the matching change in the program.

How does an LDR sensor work?

An LDR sensor changes its resistance depending on the amount of light. In the dark its resistance becomes higher, and in stronger light it becomes lower. That is why the LDR is usually connected with an ordinary resistor to form a voltage divider.

How it works:

The LDR and the resistor together form a voltage divider. The ESP32 measures the voltage at the midpoint of the divider on an analog input pin, and from that value we know how much light there is.

Why use a voltage divider?

The ESP32 does not measure resistance directly, it measures voltage. That is why the LDR is connected with a 10 kΩ resistor, so the change in resistance becomes a changing voltage that the microcontroller can read on an analog input pin.

When there is more light, the resistance of the LDR drops and the input voltage changes. When there is less light, the resistance rises and the reading changes too. This way the ESP32 gets information about the light intensity.

Pins used

In this exercise we use the following connections:

  • 3V3 – circuit supply
  • GND – common ground
  • GPIO34 – analog input for reading the voltage from the divider
  • LDR – the sensor whose resistance changes with light
  • 10 kΩ resistor – the other part of the voltage divider
Note:

The ESP32 uses 3.3 V logic, so the analog circuit must be powered from 3V3, not from 5 V. For analog readings it is best to use ADC1 pins, such as GPIO34, because they work reliably together with Wi-Fi.

Analog reading on the ESP32

Unlike the Arduino UNO, the ESP32 has a 12-bit ADC, so analogRead() usually returns values from 0 to 4095. This means the ESP32 can detect finer changes in voltage.

In practice this allows more precise tracking of light changes. However, because of the characteristics of the ESP32 ADC, the values can be slightly nonlinear, so calibration is useful for more serious projects.

Important:

On the ESP32 we usually do not use analogWrite() as on the Arduino UNO. To control an LED or another output, we use a PWM channel through the LEDC functions (see the example below).

Formula for the voltage

To calculate the voltage on the analog input, we can use the formula:

Voltage = (ADC Value / 4095) × 3.3

Where:

  • ADC Value – the value returned by analogRead()
  • 4095 – the maximum value of the 12-bit ADC on the ESP32
  • 3.3 – the reference voltage of the ESP32 board

Why use the ESP32?

The ESP32 has many features and is an excellent choice for projects with sensors and the Internet of Things.

  • ✅ Built-in Wi-Fi
  • ✅ Built-in Bluetooth
  • ✅ More GPIO pins
  • ✅ 12-bit ADC for more detailed measurement
  • ✅ Suitable for IoT and automation

Materials needed

  • ESP32 development board
  • LDR sensor
  • 10 kΩ resistor
  • Breadboard
  • Jumper wires
  • USB cable for connecting to the computer
  • Arduino IDE with ESP32 support installed

Safety recommendations

  • Always use a 3.3 V supply for the analog part of the circuit.
  • Check that the analog input is correctly connected to the midpoint of the voltage divider.
  • Never apply more than 3.3 V to the ESP32 analog inputs.
  • For more stable values, take several readings in a row and calculate the average.

Example program for the ESP32

The following program reads the value of the LDR sensor and shows it in the Serial Monitor. If you want, the reading can also be used to control an LED with PWM.

// Pin connected to the midpoint of the voltage divider
// (junction between the LDR sensor and the 10 kΩ resistor)
#define LDR_PIN 34

void setup() {

  // Start serial communication at 115200 bps
  // so we can watch the readings in the Serial Monitor
  Serial.begin(115200);

  // The ESP32 has a 12-bit ADC,
  // so analogRead() returns values from 0 to 4095
  analogReadResolution(12);
}

void loop() {

  // Read the analog voltage on GPIO34
  // The value represents the light level
  int ldrValue = analogRead(LDR_PIN);

  // Convert the ADC value (0–4095)
  // into the PWM range (255–0)
  // The reversed range means a larger value
  // stands for weaker light (darkness)
  int brightness = map(ldrValue, 0, 4095, 255, 0);

  // Show the original ADC value
  Serial.print("LDR: ");
  Serial.print(ldrValue);

  // Show the mapped value
  Serial.print("  PWM: ");
  Serial.println(brightness);

  // Half-second pause between two readings
  delay(500);
}
How does the map() function work?

The map() function converts a number from one range into another. In this example the ADC values (0–4095) are converted into the range 255–0. Such values are often used for PWM control of LEDs, where a larger PWM value means a brighter LED.
ESP32 and LDR sensor

Light level in the Serial Monitor

LDR sensor exercise: measuring light and displaying the data.

Example: controlling an LED with PWM

If we want the LED to shine brighter in the dark and dimmer in the light, we can use a PWM (Pulse Width Modulation) signal. PWM changes the average power delivered to the LED and so controls its brightness.

ESP32 and LDR sensor

Practical setup

ESP32 connected to an LDR sensor while measuring light.

Arduino style (analogWrite)

If you have worked with the Arduino UNO before, you have probably used the analogWrite() function to generate a PWM signal.

// Analog input connected to the LDR sensor
#define LDR_PIN 34

// LED connected to GPIO2
#define LED_PIN 2

void setup() {

  Serial.begin(115200);

  // Set the LED pin as an output
  pinMode(LED_PIN, OUTPUT);

  // The ESP32 has a 12-bit ADC
  analogReadResolution(12);
}

void loop() {

  // Read the value of the LDR sensor
  int ldrValue = analogRead(LDR_PIN);

  // Convert the value into the range 255–0
  int brightness = map(ldrValue, 0, 4095, 255, 0);

  // Send the PWM value to the LED
  analogWrite(LED_PIN, brightness);

  delay(100);
}
Why is analogWrite() usually not used on the ESP32?

On the Arduino UNO the analogWrite() function automatically generates a PWM signal on certain pins. The ESP32 has much more advanced PWM hardware with several independent channels, frequencies and resolutions. Newer versions of the ESP32 libraries do support analogWrite(), but in the background it uses the LEDC (LED Control) module. Because of the differences between library versions and the greater flexibility of the ESP32, most projects use the LEDC functions directly.

ESP32 and LDR sensor

Light level in the Serial Monitor

LDR sensor exercise: measuring light and displaying the data.


Recommended ESP32 way (LEDC PWM)

// Analog input connected to the LDR sensor
#define LDR_PIN 34

// Pin connected to the LED
#define LED_PIN 2

// PWM channel we use
#define PWM_CHANNEL 0

// PWM frequency (5000 Hz)
#define PWM_FREQ 5000

// PWM resolution (8 bits = range 0–255)
#define PWM_RES 8

void setup() {

  // Start serial communication
  Serial.begin(115200);

  // The ESP32 has a 12-bit ADC
  analogReadResolution(12);

  // Create the PWM channel
  ledcSetup(PWM_CHANNEL, PWM_FREQ, PWM_RES);

  // Attach the PWM channel to GPIO2
  ledcAttachPin(LED_PIN, PWM_CHANNEL);
}

void loop() {

  // Read the current value of the LDR sensor
  int ldrValue = analogRead(LDR_PIN);

  // Convert the ADC value into the range 255–0
  int brightness = map(ldrValue, 0, 4095, 255, 0);

  // Send the PWM value to the LED
  ledcWrite(PWM_CHANNEL, brightness);

  // Show the data in the Serial Monitor
  Serial.print("LDR: ");
  Serial.print(ldrValue);

  Serial.print("  Brightness: ");
  Serial.println(brightness);

  delay(100);
}
Note for newer versions (ESP32 Arduino core 3.x): the functions ledcSetup() and ledcAttachPin() were removed. Instead, write ledcAttach(LED_PIN, PWM_FREQ, PWM_RES); in setup() and ledcWrite(LED_PIN, brightness); in loop() – the first parameter is the pin number, not the channel. You can see the version in the Boards Manager (package “esp32 by Espressif Systems”).
How does the mapping work?

The map() function converts the ADC values (0–4095) into the PWM range (255–0). Because we want the LED to shine brighter in the dark, the range is reversed. Less light gives a larger PWM value, so the LED shines brighter.

Conclusion

With an LDR sensor and an ESP32 board we can easily measure the light level in a room. This exercise is a great introduction to analog sensors, voltage dividers and PWM control of outputs on the ESP32 platform.

Connecting the LDR sensor to the ESP32 board

The LDR sensor is connected to the ESP32 board through a voltage divider made of the LDR and a 10 kΩ resistor. This way the change in the sensor's resistance becomes a change in voltage that the ESP32 can read on an analog input.

In this exercise we use GPIO34 as the analog input. This pin belongs to the ADC1 group and is well suited for analog measurements.

Pin by pin

ESP32 pin Component Connection
3V3 LDR One end of the LDR sensor is connected to the 3.3 V supply.
GPIO34 Divider midpoint Connected between the LDR sensor and the 10 kΩ resistor.
GND 10 kΩ resistor The other end of the resistor is connected to ground.

When wiring, make sure the analog input GPIO34 is connected exactly to the junction between the LDR sensor and the resistor. That is where the changing voltage appears, which the ESP32 measures with analogRead().

Wiring order:

3V3 → LDR → GPIO34 → 10 kΩ resistor → GND

When the light is strong, the resistance of the LDR drops and the voltage on GPIO34 changes. When the light is weak, the resistance rises and the reading is different. Based on these changes the ESP32 determines the light level in the room.


Check your knowledge

Open the quiz

Quiz: LDR and ESP32

1. What range does analogRead() return on the ESP32 (12-bit ADC)?




2. What voltage is the LDR divider connected to on the ESP32?




3. Which pin is used as the analog input in the lesson?




4. How is the LED brightness set on the ESP32?