Choosing Your Arduino Light Sensor: Analog LDR vs. Digital BH1750
The most common mistake makers make is trying to use a cheap analog LDR to measure precise light levels. LDRs are highly non-linear and temperature-dependent. The BH1750, on the other hand, contains an integrated photodiode and an analog-to-digital converter that outputs a calibrated lux value directly over I2C.| Feature | Analog LDR Module (GL5528) | Digital BH1750 (GY-302 Breakout) |
|---|---|---|
| Output Type | Analog voltage (0-5V) or Digital HIGH/LOW | Digital I2C (16-bit integer) |
| Measurement Unit | Raw ADC (0-1023) / Relative resistance | Direct Lux (1 to 65,535 lx) |
| Linearity | Highly non-linear (logarithmic) | Linear across measurement range |
| Typical Cost (2026) | ~$1.50 USD | ~$3.00 USD |
| Best Use Case | Day/Night threshold switching | Screen backlight dimming, grow tents, lux logging |
Hardware Build: Parts List and Pin Mapping
This build targets the Arduino Uno R3 (or any ATmega328P-based board like the Nano v3). We will wire both sensors simultaneously so you can compare the raw analog data against the calibrated digital lux readings in the Serial Monitor.Exact Parts List
- Microcontroller: Arduino Uno R3 (ATmega328P, 5V logic)
- Digital Sensor: GY-302 BH1750FVI I2C Breakout Module
- Analog Sensor: LM393 LDR Module (with both AO and DO pins)
- Wiring: Male-to-male and male-to-female jumper wires (22 AWG)
- Breadboard: Standard 830-point solderless breadboard
Pin Mapping Table
| Arduino Uno R3 Pin | BH1750 (GY-302) Pin | LDR Module Pin | Notes |
|---|---|---|---|
| 5V | VCC | VCC | BH1750 has an onboard 3.3V LDO; 5V is safe. |
| GND | GND | GND | Common ground is mandatory for I2C and ADC. |
| A4 (SDA) | SDA | - | I2C Data line. Onboard 4.7k pull-ups included. |
| A5 (SCL) | SCL | - | I2C Clock line. |
| - | ADDR | - | Leave floating/LOW for I2C address 0x23. |
| A0 | - | AO | Analog Out from the LDR voltage divider. |
| D2 | - | DO | Digital Out (optional, triggers at trimpot threshold). |
Complete Code: Reading Lux and Raw ADC Values
Before compiling, install the required library. Open the Arduino IDE Library Manager (Ctrl+Shift+I), search forBH1750, and install the library by claws (usually version 1.3.0 or newer). For more on I2C communication protocols, refer to the official Arduino Wire library documentation.
The following code initializes the I2C bus, verifies the BH1750 is responding, and reads both the digital lux value and the analog LDR voltage divider output. It includes explicit error handling for sensor initialization failures.
#include <Wire.h>
#include <BH1750.h>
// --- Pin Definitions ---
const int PIN_LDR_ANALOG = A0;
const int PIN_LDR_DIGITAL = 2;
// Initialize the BH1750 sensor object
BH1750 lightMeter;
void setup() {
Serial.begin(115200);
while (!Serial) { delay(10); } // Wait for serial port (Leonardo/Micro)
pinMode(PIN_LDR_DIGITAL, INPUT);
// Initialize I2C bus
Wire.begin();
// Attempt to initialize the BH1750 sensor
// The default I2C address is 0x23. Use 0x5C if ADDR pin is pulled HIGH.
if (lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE, 0x23, &Wire)) {
Serial.println(F("[BH1750] Initialized successfully."));
} else {
Serial.println(F("[BH1750] ERROR: Sensor not found on I2C bus. Check wiring."));
// Halt execution if the primary digital sensor is missing
while (1) { delay(1000); }
}
}
void loop() {
// Read Digital BH1750 Sensor
float lux = lightMeter.readLightLevel();
// Read Analog LDR Sensor (10-bit ADC, 0-1023)
int ldrRaw = analogRead(PIN_LDR_ANALOG);
// Calculate LDR voltage (assuming 5V reference)
float ldrVoltage = ldrRaw * (5.0 / 1023.0);
// Read Digital LDR Threshold Pin
int ldrDigital = digitalRead(PIN_LDR_DIGITAL);
// Print formatted data to Serial Monitor
Serial.print(F("Lux: "));
Serial.print(lux, 1);
Serial.print(F(" lx | LDR Raw: "));
Serial.print(ldrRaw);
Serial.print(F(" ("));
Serial.print(ldrVoltage, 2);
Serial.print(F("V) | LDR Threshold: "));
Serial.println(ldrDigital == HIGH ? F("DARK") : F("LIGHT"));
// The BH1750 in continuous high-res mode takes ~120ms per reading
delay(250);
}
analogRead() function relies on a stable reference voltage. If you are powering your Arduino Uno via USB, the 5V rail can fluctuate between 4.7V and 5.1V depending on your PC's USB port. For precision LDR voltage calculations, power the Uno via the DC barrel jack with a regulated 9V supply, or use the analogReference(INTERNAL) command to switch to the stable 1.1V internal reference (requiring a different voltage divider ratio).
Debugging: "Sensor not found" and Erratic Readings
When working with I2C sensors and analog dividers, failures usually fall into two categories: compiler errors and runtime bus failures.1. Compiler Error: Missing Library
Exact Error String: fatal error: BH1750.h: No such file or directory
Fix: You skipped the library installation. Go to Sketch > Include Library > Manage Libraries, search for "BH1750" by claws, and install it. Do not attempt to download raw GitHub ZIPs unless you know how to manually place them in your Documents/Arduino/libraries folder.
2. Runtime Error: I2C Bus Failure
Exact Error String: [BH1750] ERROR: Sensor not found on I2C bus. Check wiring.
If this prints to your Serial Monitor and the code halts, the ATmega328P cannot communicate with the sensor. Here are the first three things to check, ranked by probability:
- I2C Address Mismatch (Most Likely): The GY-302 module has an
ADDRpin. If left unconnected or tied to GND, the address is0x23. If accidentally tied to VCC or 3.3V, the address shifts to0x5C. Check your physical wiring and update thelightMeter.begin()parameter in the code to match. - SDA/SCL Crossed or Missing Pull-ups: I2C requires pull-up resistors on the SDA and SCL lines. The GY-302 breakout includes onboard 4.7kΩ pull-ups. However, if you are using jumper wires longer than 12 inches (30cm), bus capacitance increases, causing signal degradation. Add external 4.7kΩ pull-up resistors from A4 and A5 to 5V, or shorten your wires. Verify SDA goes to A4 and SCL goes to A5 (a common mistake is swapping them).
- VCC Brownout: While the BH1750 chip itself runs at 3.3V, the GY-302 breakout board usually features an LDO voltage regulator allowing 5V input. If you are using a clone board with a missing or faulty LDO, feeding it 5V will brownout the chip. Measure the VCC pin at the sensor header with a multimeter; it should read a stable 5.0V (or 3.3V if you are using a raw chip breakout).
3. Erratic LDR Analog Readings
If your BH1750 is stable but the LDR raw values are jumping wildly (e.g., 450, 812, 310, 900), you are likely picking up 50Hz/60Hz mains hum from nearby AC wiring. The analog input acts as an antenna. Fix this by adding a 0.1µF (100nF) ceramic capacitor between the A0 pin and GND right at the breadboard to filter high-frequency noise, or use a software moving-average filter in your C++ code.
Extending and Simplifying the Build
Not every project requires a full I2C implementation and Serial logging. Here is how to adapt this circuit based on your actual project requirements.How to Simplify (The "No-Code" Digital Trigger)
If you are building a solar-powered garden light or a simple dark-activated relay, you do not need the BH1750 or the AO pin on the LDR module.
Look closely at your LDR module; it features a small blue trimpot (variable resistor) and an LM393 comparator chip.
Wire only VCC, GND, and the DO (Digital Out) pin to Arduino D2. Use a small Phillips screwdriver to turn the trimpot while covering the LDR with your hand. When the LED on the module toggles at your desired darkness threshold, lock it in. Your code is now reduced to a simple if (digitalRead(2) == HIGH) { turnOnLights(); }. No analog math required.
How to Extend (Data Logging and Displays)
To turn this into a standalone grow-tent light monitor:
- Add an OLED: Wire an SSD1306 128x64 I2C OLED display to the exact same A4/A5 pins. I2C is a bus; you can daisy-chain devices. Use the
Adafruit_SSD1306library to render the lux value in large text. - Add WiFi: Swap the Arduino Uno R3 for an ESP32 DevKit v1. The I2C pins will change (usually GPIO 21 for SDA and GPIO 22 for SCL on standard ESP32 boards). Use the
PubSubClientlibrary to publish the lux data to an MQTT broker like Home Assistant.
Frequently Asked Questions
How do I calibrate an analog Arduino light sensor to read in Lux?
You cannot natively calibrate an LDR to output true lux because its resistance curve is logarithmic and varies by manufacturing batch. However, you can create a software lookup table. Download a Lux Meter app on your smartphone (which uses its own calibrated ambient light sensor). Place the phone and your LDR under the exact same light source. Record the app's lux reading and the Arduino's analogRead() value at 5 different light levels (pitch black, dim room, office lighting, overcast outdoor, direct sun). Use the Arduino map() function or a piecewise linear interpolation array in your code to translate the raw ADC values into estimated lux.
Why is my BH1750 Arduino light sensor reading stuck at 54612 lux?
A reading of exactly 54612 lux (or sometimes 65535) indicates sensor saturation or an I2C bus lockup. First, check your environment: the BH1750 maxes out around 65k lux, and direct, unfiltered sunlight can easily exceed this, pegging the internal ADC. If you are indoors and still seeing this, the I2C bus has likely locked up due to a missing ACKnowledge (ACK) bit. Power cycle both the Arduino and the sensor. If the issue persists, add the lightMeter.setMTreg(69) command in your setup() to lower the measurement time and increase the sensor's resolution range, preventing internal register overflow.
Can I wire multiple Arduino light sensors to the same I2C bus?
Yes, but with hardware limits. The BH1750 has a hardcoded address selection pin (ADDR). If the ADDR pin is LOW, the I2C address is 0x23. If HIGH, it is 0x5C. This means you can wire exactly two BH1750 sensors to a single Arduino I2C bus without extra hardware. If you need to monitor light in four different corners of a room, you will need to use an I2C multiplexer like the TCA9548A, which allows you to route the I2C signal to up to 8 separate channels, each hosting its own 0x23 sensor.






