The BH1750 is a digital ambient light sensor that measures illuminance (light intensity) in lux and communicates over I2C. Unlike a simple photoresistor (LDR), the BH1750 outputs a calibrated digital value directly in lux, so there is no need for analog-to-digital conversion or manual calibration. It is an ideal sensor for projects that need accurate, repeatable light measurements.
This guide uses the ESP32-WROOM-32 DevKit. Pin labels may differ on your board -- check your board's pinout diagram.
Alternatives: For simple light/dark detection where calibrated lux values are not needed, an LDR is cheaper and simpler (no library, no I2C). See our light sensor comparison for a full breakdown.
🔗Key Specs
| Parameter | Value |
|---|---|
| Measurement range | $1$ to $65535\,\text{lx}$ |
| Resolution | $1\,\text{lx}$ (high-resolution mode) |
| Accuracy | Varies; close to human eye response |
| Supply voltage | $2.4$ to $3.6\,\text{V}$ |
| Interface | I2C |
| I2C address | 0x23 (ADDR low) or 0x5C (ADDR high) |
🔗What Is Lux?
Lux (lx) is the standard unit of illuminance -- it measures how much light falls on a surface. Here are some reference values to give you an intuitive sense:
| Condition | Approximate Lux |
|---|---|
| Direct sunlight | $30{,}000 - 100{,}000\,\text{lx}$ |
| Overcast daylight | $10{,}000 - 25{,}000\,\text{lx}$ |
| Office lighting | $300 - 500\,\text{lx}$ |
| Living room | $100 - 300\,\text{lx}$ |
| Twilight | $\approx 10\,\text{lx}$ |
| Full moon | $\approx 0.25\,\text{lx}$ |
These values are useful for setting thresholds in your projects -- for example, turning on a light when the reading drops below $200\,\text{lx}$.
🔗What You'll Need
| Component | Qty | Notes | Buy |
|---|---|---|---|
| ESP32 dev board | 1 | AliExpress | Amazon.de .co.uk .com | |
| BH1750 light sensor | 1 | AliExpress | Amazon.de .co.uk .com | |
| Breadboard | 1 | AliExpress | Amazon.de .co.uk .com | |
| Jumper wires | 5 | AliExpress | Amazon.de .co.uk .com |
Links marked Amazon/AliExpress are affiliate links. We may earn a small commission at no extra cost to you.
🔗Wiring (I2C)
| BH1750 Pin | ESP32 Pin | Notes |
|---|---|---|
| VCC | 3.3V | Do not use 5V |
| GND | GND | |
| SDA | GPIO 21 | Default I2C data line on ESP32 |
| SCL | GPIO 22 | Default I2C clock line on ESP32 |
| ADDR | GND | Sets address to 0x23 (connect to VCC for 0x5C) |
Pin labels, GPIO numbers, and ADC channels vary between ESP32 boards. GPIOs 21 (SDA) and 22 (SCL) are the default I2C pins on most ESP32-WROOM-32 DevKit boards, but your board may differ. Always check your board's pinout diagram.
🔗I2C Address
The ADDR pin controls the I2C address:
- ADDR connected to GND (or left floating): address is
0x23 - ADDR connected to VCC: address is
0x5C
This lets you use two BH1750 sensors on the same I2C bus with different addresses.
🔗Required Libraries
Install the following through the Arduino Library Manager:
- BH1750 by Christopher Laws -- provides a straightforward API for the sensor
To install:
- Go to Sketch > Include Library > Manage Libraries...
- Search for BH1750 and install the library by Christopher Laws
No additional dependencies are needed beyond the built-in Wire.h library.
🔗Code Example
This sketch reads the light level in lux from the BH1750 sensor and prints it to the Serial Monitor every second.
#include <Wire.h>
#include <BH1750.h>
BH1750 lightMeter;
void setup() {
Serial.begin(115200);
Wire.begin(); // Initialize I2C (SDA = GPIO 21, SCL = GPIO 22)
if (lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE)) {
Serial.println("BH1750 sensor initialized.");
} else {
Serial.println("Error: Could not find BH1750 sensor.");
Serial.println("Check wiring and I2C address.");
while (1) delay(10); // Halt
}
}
void loop() {
float lux = lightMeter.readLightLevel();
if (lux < 0) {
Serial.println("Error reading light level.");
} else {
Serial.print("Light: ");
Serial.print(lux, 1);
Serial.println(" lx");
}
delay(1000);
}🔗Using Two Sensors
If you connect two BH1750 sensors with different ADDR settings, you can read both:
BH1750 lightMeter1(0x23); // ADDR = GND
BH1750 lightMeter2(0x5C); // ADDR = VCC
void setup() {
Serial.begin(115200);
Wire.begin();
lightMeter1.begin(BH1750::CONTINUOUS_HIGH_RES_MODE);
lightMeter2.begin(BH1750::CONTINUOUS_HIGH_RES_MODE);
}
void loop() {
float lux1 = lightMeter1.readLightLevel();
float lux2 = lightMeter2.readLightLevel();
Serial.print("Sensor 1: ");
Serial.print(lux1, 1);
Serial.print(" lx | Sensor 2: ");
Serial.print(lux2, 1);
Serial.println(" lx");
delay(1000);
}🔗How It Works
The BH1750 contains an integrated photodiode and a 16-bit analog-to-digital converter. It measures the intensity of visible light hitting the sensor and converts it to a digital lux value internally. The sensor's spectral response is designed to approximate the sensitivity of the human eye, which makes its readings feel intuitive.
In continuous high-resolution mode (the default in our code), the sensor takes a new reading approximately every $120\,\text{ms}$ and makes the latest value available over I2C whenever you request it.
🔗Troubleshooting
| Problem | Possible Cause | Solution |
|---|---|---|
| "Could not find BH1750" | Wrong I2C address | Check the ADDR pin; try 0x5C if ADDR is connected to VCC |
| "Could not find BH1750" | Wiring issue | Verify SDA, SCL, VCC, and GND connections |
| Always reads 0 lx | Sensor in power-down mode | Ensure begin() is called with a valid mode |
| Readings are very noisy | Electrical interference | Use shorter I2C wires; add pull-up resistors if not on module |
| Reads max value (65535 lx) | Sensor saturated by very bright light | Normal for direct sunlight; consider shading the sensor |
| Reads negative value | I2C communication error | Check wiring; reduce I2C clock speed |
🔗Next Steps
- Build an automatic desk lamp that adjusts brightness based on ambient light levels
- Create a daylight logger that tracks light levels throughout the day
- Combine the BH1750 with a relay to control outdoor lights at dusk
- Pair it with a BME280 for a complete environmental monitoring station