How the BH1750 Digital Light Sensor Actually Works
The BH1750FVI, designed by ROHM Semiconductor, is a digital ambient light sensor IC that integrates a photodiode, a transimpedance amplifier, and an integrating analog-to-digital converter (ADC) into a single 3.0 x 1.6 mm package. Unlike analog CdS photoresistors that change resistance, or raw photodiodes that output a microamp current requiring external op-amps, the BH1750 handles the signal conditioning internally. It measures visible light (roughly 380nm to 650nm, peaking at 550nm) and natively rejects infrared and ultraviolet radiation, making its spectral response closely match the human eye's photopic luminosity function.
The output is strictly digital via an I2C interface. It does not output a variable voltage or current. Instead, it transmits a 16-bit unsigned integer representing the calculated illuminance. This eliminates the need for external ADCs, reference voltage calibration, or analog noise filtering on the microcontroller side, provided your I2C bus is properly terminated with pull-up resistors. You can read the ROHM BH1750FVI datasheet on Mouser for the exact internal block diagram and timing characteristics.
Wiring the BH1750 to ESP32 and Arduino
Most hobbyists use the GY-30 or GY-302 breakout boards rather than the bare IC. These breakouts typically include a 3.3V LDO regulator and 4.7kΩ I2C pull-up resistors, allowing you to power them from either a 3.3V or 5V pin on an Arduino Uno. However, if you are wiring a bare BH1750FVI chip or using a 3.3V-native microcontroller like the ESP32 or Raspberry Pi Pico, you must respect the strict supply voltage limits.
| BH1750 Pin | Breakout Label | ESP32 Connection | Arduino Uno Connection | Function & Notes |
|---|---|---|---|---|
| VCC | VCC / VIN | 3.3V | 5V (if LDO present) or 3.3V | Supply range: 2.4V to 3.6V (bare IC). |
| GND | GND | GND | GND | Common ground reference. |
| SCL | SCL | GPIO 22 | A5 | I2C Clock. Needs 4.7kΩ pull-up to VCC. |
| SDA | SDA | GPIO 21 | A4 | I2C Data. Needs 4.7kΩ pull-up to VCC. |
| ADDR | ADDR | GND (default) | GND (default) | Address select. LOW = 0x23, HIGH = 0x5C. |
Output Math: Converting Raw I2C Bytes to Lux
Because the BH1750 outputs a digital value, you do not need to perform any analog voltage-to-lux math. The sensor's internal ADC handles the integration. However, you must scale the raw 16-bit integer based on the measurement mode you configure via I2C commands.
When operating in the default Continuous High-Resolution Mode (I2C command 0x10), the sensor integrates light for approximately 120ms. The raw 16-bit value returned by the sensor represents the lux value multiplied by 1.2. Therefore, the raw-to-unit math is:
Illuminance (lux) = Raw_16bit_Value / 1.2
If you need higher precision for very dark environments, you can switch to Continuous High-Resolution Mode 2 (I2C command 0x11). This doubles the integration time to ~120ms but halves the lux count resolution to 0.5 lux. The math shifts slightly:
Illuminance (lux) = (Raw_16bit_Value / 1.2) / 2
For fast-tracking applications where precision is less critical, Continuous Low-Resolution Mode (I2C command 0x13) drops the integration time to ~16ms. The scaling factor remains 1.2, but the resolution drops to 4 lux per count, and the sensor ignores light levels below 4 lux.
Real-World Interference and Calibration
While the BH1750 is highly accurate for daylight and standard indoor lighting, real-world deployments introduce interference that requires software or hardware mitigation.
1. High-Frequency PWM Dimming: The sensor's integration window (120ms in high-res mode) is specifically timed to average out 50Hz and 60Hz AC mains flicker. However, if you are measuring light from LED strips driven by high-frequency PWM dimmers (typically 1kHz to 20kHz), the sensor's integration window can alias with the PWM frequency. This results in wildly fluctuating lux readings. The fix is to implement a software moving average filter (e.g., averaging 10 consecutive readings) or switch to a DC-constant-current LED driver.
2. Spectral Mismatch and Calibration: The BH1750 is calibrated to the CIE standard illuminant (daylight). If you are using it to measure specialized lighting—such as magenta/purple LED grow lights, low-pressure sodium vapor lamps, or deep red darkroom lighting—the raw lux reading will be inaccurate because the sensor's photodiode sensitivity drops off sharply outside the 450nm–600nm range. For horticulture or specialized industrial lighting, you must apply a custom empirical scaling factor derived from a calibrated reference lux meter.
3. Optical Attenuation: If you mount the sensor behind an enclosure window, the glass or acrylic will attenuate the light. Standard clear glass transmits about 90-92% of visible light, while UV-blocking or tinted polycarbonate can drop transmission to 70% or lower. You must multiply your final lux calculation by the inverse of the window's transmission coefficient (e.g., Lux_Final = Lux_Raw * 1.08 for standard glass).
BH1750 Light Sensor FAQ
Why is my BH1750 light sensor stuck reading exactly 54612 lux?
A constant reading of 54612 lux (or sometimes 54612.5) means the sensor's internal ADC is maxed out. The BH1750 outputs a maximum 16-bit raw value of 65535. When you divide 65535 by the 1.2 scaling factor, you get 54612.5 lux. This happens for two reasons: either the sensor is pointed directly at the sun or a high-lumen halogen lamp (exceeding its ~54k lux physical ceiling), or there is an I2C bus error where the SDA line is being pulled high constantly due to missing pull-up resistors, causing the microcontroller to read 0xFFFF continuously. Check your pull-ups and move the sensor out of direct, unfiltered sunlight.
How do I change the I2C address of the BH1750 light sensor?
By default, with the ADDR pin floating or tied to GND, the BH1750 listens at I2C address 0x23. If you need to connect two BH1750 sensors to the same I2C bus (for example, one facing inward and one facing outward in a smart blind system), you must change the address of the second sensor. To do this, physically connect the ADDR pin on the second sensor's breakout board to the VCC pin. This pulls the address select line high, shifting the I2C address to 0x5C. You can verify the new addresses by running an I2C scanner sketch on your Arduino or ESP32.
Can I use the BH1750 light sensor outdoors in direct sunlight?
You can, but with caveats regarding saturation and thermal drift. As noted, direct midday sunlight can exceed 100,000 lux, which will saturate the sensor and cap the reading at ~54,612 lux. Furthermore, the BH1750 datasheet specifies an operating temperature range of -40°C to +85°C. In an outdoor enclosure subjected to direct solar loading, internal temperatures can easily exceed 60°C, which introduces thermal noise into the transimpedance amplifier. For outdoor meteorological stations, it is better to use a dedicated, cosine-corrected pyranometer or place the BH1750 inside a ventilated, UV-stable radiation shield (like a Stevenson screen) to measure ambient skylight rather than direct solar irradiance.
For more practical implementation details and library setups, the Adafruit BH1750 Ambient Light Sensor guide provides excellent wiring diagrams and CircuitPython/Arduino code examples.






