To build a reliable light sensor Arduino project, skip the raw analog Light Dependent Resistors (LDRs) and use the BH1750FVI digital I2C sensor (commonly sold as the GY-302 module). Unlike an LDR that requires messy voltage-divider math and manual calibration, the BH1750FVI outputs calibrated lux values directly over I2C, compensating for temperature and spectral variations internally. This guide targets the Arduino Uno R4 Minima (Part #ABX00080), utilizing its native 3.3V I2C logic to interface safely with the sensor without frying the bus.
Sensor Selection: BH1750FVI vs. Analog LDR vs. TSL2561
Before wiring anything, it is critical to understand why we are choosing the BH1750FVI over cheaper or more complex alternatives. The table below breaks down the real-world bench characteristics of the three most common ambient light sensors.
| Feature | BH1750FVI (GY-302) | GL5528 Analog LDR | TSL2561 (Adafruit 439) |
|---|---|---|---|
| Output Type | Digital I2C (Direct Lux) | Analog Resistance (Voltage Divider) | Digital I2C (Raw IR + Visible) |
| Resolution | 1 lux steps (High Res Mode) | Highly non-linear, ~50 lux variance | 0.1 lux (with 16x gain) |
| Spectral Response | Matches human eye (CIE 1931) | Peaks at 540nm (green bias) | Requires manual IR compensation math |
| I2C Address | 0x23 (ADDR low) / 0x5C (ADDR high) | N/A (Analog Pin) | 0x29, 0x39, or 0x49 |
| Typical Price (2026) | $2.50 - $4.00 (Clone modules) | $0.10 - $0.50 (Raw component) | $6.95 (Official Adafruit breakout) |
Parts List & Pin Mapping Specification
This build specifically uses the Arduino Uno R4 Minima. Unlike the classic Uno R3 (which uses 5V logic on its I2C pins), the R4 Minima's I2C bus is tied to the 3.3V rail via the internal RA4M1 chip. This prevents overvolting the BH1750FVI, which has an absolute maximum VCC rating of 4.5V and I2C pin tolerance of VCC + 0.3V.
Required Materials
- Microcontroller: Arduino Uno R4 Minima (ABX00080)
- Sensor Module: GY-302 BH1750FVI Breakout Board
- Wiring: 4x Male-to-Male Dupont jumper wires (22 AWG stranded)
- Breadboard: Standard 830-point solderless breadboard
- Pull-up Resistors: 2x 4.7kΩ (Only required if your specific GY-302 clone lacks onboard pull-ups; most have them pre-soldered).
Pin Mapping Table
| GY-302 Module Pin | Arduino Uno R4 Minima Pin | Function & Notes |
|---|---|---|
| VCC | 3.3V | Do NOT use the 5V pin. The module's onboard LDO (if present) drops 5V to 3.3V, but feeding 3.3V directly bypasses LDO heat and noise. |
| GND | GND (Either pin) | Common ground reference. Keep wire length under 15cm to minimize ground bounce. |
| SCL | A5 (SCL) | I2C Clock. On the R4 Minima, this is strictly 3.3V logic. |
| SDA | A4 (SDA) | I2C Data. Ensure this isn't swapped with SCL, a common breadboarding error. |
| ADDR | Leave Floating or GND | Dictates I2C address. Low/GND = 0x23. High/VCC = 0x5C. We use 0x23. |
Wiring & Assembly Steps
Follow these steps to assemble the circuit. Ensure the board is completely unpowered via USB while making connections to prevent accidental I2C bus shorts.
- Seat the Microcontroller: Place the Arduino Uno R4 Minima across the center trench of the breadboard.
- Seat the Sensor: Place the GY-302 module on the same side of the trench, ensuring the header pins are fully inserted. If the module came without headers, solder a 1x5 breakaway male header to it first.
- Connect Power: Run a jumper from the Arduino's
3.3Vpin to the GY-302VCCpin. Run a second jumper from any ArduinoGNDpin to the GY-302GNDpin. - Connect I2C Data Lines: Connect Arduino
A4to GY-302SDA. Connect ArduinoA5to GY-302SCL. - Verify Pull-ups: Flip the GY-302 module over. Look for two tiny surface-mount resistors (usually labeled 472 or 103) near the SDA/SCL traces. If they are missing (empty pads), insert 4.7kΩ through-hole resistors between SDA-VCC and SCL-VCC on the breadboard. Without pull-ups, the I2C bus will float and fail.
- Inspect and Power: Check for stray wire strands bridging the SDA and SCL rows. Plug the Arduino into your PC via USB-C.
Complete Arduino Code with I2C Error Handling
The code below utilizes the industry-standard BH1750 library by Christopher Laws. Install it via the Arduino IDE Library Manager (Search: "BH1750", select the one by claws). This sketch includes explicit I2C bus scanning and error handling to prevent the program from hanging if the sensor is disconnected.
#include <Wire.h>
#include <BH1750.h>
// Pin definitions for Arduino Uno R4 Minima
#define SDA_PIN A4
#define SCL_PIN A5
#define SENSOR_ADDR 0x23 // ADDR pin tied to GND
BH1750 lightMeter;
void setup() {
Serial.begin(115200);
while (!Serial) { delay(10); } // Wait for serial port on native USB boards
// Initialize I2C bus with explicit pins and 100kHz clock
Wire.begin(SDA_PIN, SCL_PIN);
Wire.setClock(100000);
Serial.println(F("BH1750 Light Sensor Initialization..."));
// Error Handling: Ping the I2C address before library init
Wire.beginTransmission(SENSOR_ADDR);
uint8_t i2c_error = Wire.endTransmission();
if (i2c_error != 0) {
Serial.print(F("Error: BH1750 not found on I2C bus. I2C Status Code: "));
Serial.println(i2c_error);
Serial.println(F("Halted. Check SDA/SCL wiring and pull-up resistors."));
while (1) { delay(1000); } // Halt execution safely
}
// Initialize sensor in Continuous High Resolution Mode (1 lux resolution, 120ms timing)
if (lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE, SENSOR_ADDR, &Wire)) {
Serial.println(F("Sensor initialized successfully."));
} else {
Serial.println(F("Error: Sensor initialized but failed to configure registers."));
while (1) { delay(1000); }
}
}
void loop() {
float lux = lightMeter.readLightLevel();
// Handle library-specific error codes
if (lux < 0) {
Serial.println(F("Error: I2C read failed or sensor saturated."));
} else {
Serial.print(F("Ambient Light: "));
Serial.print(lux, 1);
Serial.println(F(" lx"));
// Contextual threshold example: Turn on 'lights' if dark
if (lux < 50.0) {
Serial.println(F("-> Environment is dark. Triggering relay logic."));
}
}
delay(500); // Read twice a second
}
Debugging: I2C NACK and Zero Lux Errors
Embedded I2C is notorious for silent failures. If your Serial Monitor outputs Error: BH1750 not found on I2C bus. I2C Status Code: 2 (which translates to "received NACK on transmit of address"), or if it prints 0.0 lx constantly in a bright room, follow this ranked troubleshooting path.
The First Three Things to Check
- SDA and SCL Swapped: This accounts for 80% of I2C failures. The Arduino IDE will not throw a compile error if you wire A4 to SCL and A5 to SDA, but the hardware I2C peripheral will fail to generate a clock signal. Swap the wires and reset the board.
- VCC Level and Brownouts: If you wired VCC to the 5V pin on a board with a strict 3.3V I2C bus (like the R4 Minima or ESP32), the sensor might power up, but the 5V pull-ups on the module will feed 5V back into the 3.3V SDA/SCL pins, causing the microcontroller to reject the high state. Always verify your module's onboard voltage regulator, or just feed it 3.3V directly.
- Missing Pull-Up Resistors: I2C is an open-drain protocol. Devices pull the line LOW, but rely on resistors to pull it HIGH. If your clone GY-302 board skipped the 4.7kΩ SMD resistors to save $0.02, the bus will float. Measure the resistance between SDA and VCC with a multimeter (power off); it should read ~4.7kΩ. If it reads infinite (OL), add external pull-ups.
Wire.setClock(50000);) or use an I2C bus extender like the P82B96.
Extending and Simplifying the Build
Depending on your end goal, you can strip this project down to its bare essentials or scale it up for home automation.
How to Simplify (The Minimalist Approach)
If you only need a binary "is it dark?" trigger for a nightlight and don't care about exact lux values, ditch the BH1750FVI and use a standard GL5528 LDR with a 10kΩ fixed resistor as a voltage divider into an analog pin (A0). It costs $0.15, requires no libraries, and uses a simple analogRead(). However, you will lose temperature stability and must manually tune the threshold in code for every single unit you build due to LDR manufacturing variance.
How to Extend (The IoT Approach)
To turn this into a smart home lux logger:
- Upgrade the Brain: Swap the Uno R4 for an ESP32-C3 SuperMini. It retains the 3.3V native I2C logic but adds WiFi.
- Add MQTT: Use the
PubSubClientlibrary to publish the lux payload to an MQTT broker (like Mosquitto) every 5 minutes. - Deep Sleep: The BH1750FVI has an active current of 190µA, but in Power Down mode, it drops to 1µA. You can use the ESP32's deep sleep features, waking the sensor via I2C, taking a reading, and going back to sleep, allowing the entire node to run for months on a single 18650 Li-Ion cell.
For further reading on the sensor's internal architecture and spectral response curves, refer to the official Rohm Semiconductor BH1750FVI Datasheet. For microcontroller pinout specifics, consult the Arduino Uno R4 Minima Documentation.






