the light

BH1750 with ESP32: Measuring Light in Lux

BH1750 Project Guide with ESP32 and Arduino; Datasheet, I2C Address, Wiring, Measurement Modes, Lux Code, Calibration and Troubleshooting.

Contents

Project-oriented training of measuring the intensity of illumination

Setting up BH1750 with ESP32 and Arduino; Light measurement in lux

BH1750 is a digital ambient light sensor with deluxe direct output and I2C interface. In this guide we will review the raw chip and ready board specifications, addresses, measurement modes, wiring, practical code, and how to build a reliable lighting controller.

Actual view of the Adafruit BH1750 light sensing board with STEMMA QT connectors and input and output pins
Actual photo of Adafruit BH1750 board; By Bryan Siepert / Adafruit licensed under CC BY-SA 3.0; Resize and convert to WebP without cropping. The edited image is also under the same license.
outputBrightness in lx
Raw chip feeding2.4 to 3.6V
InterfaceI2C to 400kHz
address0x23 or 0x5C

What is BH1750 and what does luxury mean?

ROHM's BH1750FVI is a 16-bit light-to-digital data converter. Its spectral response is almost close to the human eye and the infrared effect is low. Because of this, instead of a raw ADC number, it reports brightness in lux.

Lux is the received luminous flux per square meter. This number is suitable for room light control, automatic display adjustment, greenhouse lighting recording and day and night detection; But it doesn't measure light color, UV or spectrum quality.

Important numbers of BH1750FVI datasheet

parameterOfficial amountPractical result
VCC raw chip2.4 to 3.6VThe absolute limit of 4.5V is not allowed to work normally with 5V.
I2C reference voltage1.65V to VCCThe logic level must match the actual board design.
Flow in measurementtypical 120µA, the maximum 190µAPower Down mode is useful for battery draining project.
High Resolution1lx, typical 120ms, the maximum 180msFor starters and low light, the recommended choice is the datasheet.
High Resolution Mode 20.5lx, typical 120msMore resolution with the same typical time.
Low Resolution4lx, typical 16ms, the maximum 24msIt is faster but shows small changes less.
raw data16 bits; 0 to 65535 countingRaw number is not the same as luxury; The conversion factor and measurement mode are important.

I2C address and measurement modes

When ADDR is low, the seven-bit address is 0x23, and when it is raised to VCC, the address becomes 0x5C. These two addresses allow to place two sensors on one bus. The Adafruit board has a pull-down resistance of ten kilo-ohms for the default pull-down mode and for SCL and SDA.

In Continuous mode, the result is updated with the interval of the selected mode. In One Time, the sensor will go to Power Down after completing the automatic measurement. The datasheet for High Resolution considers it necessary to wait up to 180ms; An immediate read may return the previous value. The hp_BH1750 library handles this scheduling with a non-blocking API.

Wiring BH1750 with ESP32 and Arduino Uno

Adafruit BH1750 boardGeneric ESP32Arduino Uno
VIN3V35V for documented Adafruit board only
GNDGNDGND
SDAGPIO21A4 / SDA
SCLGPIO22A5 / SCL
ADDRDown for 0x23Down for 0x23

GPIO21 and GPIO22 are the common default of Generic ESP32, not the rule of all boards. In ESP32-S3, ESP32-C3 and custom design replace the actual board pins. Keep strings short and avoid repetitive pull-ups on sensitive bass.

Step-by-step setup in Arduino IDE

  1. Search for hp BH1750 in Library Manager and install the Stefan Armborst library.
  2. Wire the board without power and check the voltage level of the module.
  3. Run the i2c-scanner.ino code; 0x23 or 0x5C should be seen.
  4. Upload the original code and open Serial Monitor to 115200 baud.
  5. Take multiple samples and use separate round trip thresholds for control decision.

BH1750 code for ESP32 and Arduino

#include <Wire.h>
#include <hp_BH1750.h>

#if defined(ESP32)
constexpr int SDA_PIN = 21;
constexpr int SCL_PIN = 22;
#endif

hp_BH1750 lightSensor;
unsigned long measurementStarted = 0;

void startMeasurement() {
  if (!lightSensor.start(BH1750_QUALITY_HIGH, BH1750_MTREG_DEFAULT)) {
    Serial.println(F("I2C start failed; check wiring and reset."));
    while (true) delay(10);
  }
  measurementStarted = millis();
}

void setup() {
  Serial.begin(115200);
#if defined(ESP32)
  if (!Wire.setPins(SDA_PIN, SCL_PIN)) {
    Serial.println(F("Invalid I2C pins."));
    while (true) delay(10);
  }
#endif

  if (!lightSensor.begin(BH1750_TO_GROUND)) {
    Serial.println(F("BH1750 not found; check wiring and address."));
    while (true) delay(10);
  }

  Wire.setClock(100000);
  startMeasurement();
}

void loop() {
  if (lightSensor.hasValue()) {
    const float lux = lightSensor.getLux();
    // hp_BH1750 1.0.2 sets getTime() to 999 on an I2C read error.
    if (lightSensor.getTime() == 999) {
      Serial.println(F("I2C read failed; check wiring and reset."));
      while (true) delay(10);
    } else if (lightSensor.saturated()) {
      Serial.println(F("Full-scale reading: check bright light and I2C."));
    } else {
      Serial.print(F("Illuminance: "));
      Serial.print(lux, 1);
      Serial.println(F(" lx"));
    }
    startMeasurement();
  } else if (millis() - measurementStarted > 2000UL) {
    Serial.println(F("Measurement timeout; check wiring and reset."));
    while (true) delay(10);
  }
  delay(1);
}

Code analysis and address selection

  • BH1750_TO_GROUND selects address 0x23; Use BH1750_TO_VCC for ADDR connected to VCC.
  • hasValue() checks that the conversion has timed out without suspending the loop.
  • getLux() reads the value converted to lux and start() starts the next instance.
  • Wire.setPins() sets the ESP32 pins before starting the library; begin() itself initializes the Wire library and then the bus speed is set.
  • start(BH1750_QUALITY_HIGH, BH1750_MTREG_DEFAULT) explicitly selects High mode with MTreg=69; The library's built-in default value is High2.
  • Measurement start error, full scale data and waiting more than two seconds have diagnostic messages. Two seconds is the software guard limit for this example, not the datasheet conversion time.
  • In the version 1.0.2 implementation, the I2C receive error returns the value of getTime() to 999. The code checks this symbol before printing the lux so that the cable break is not mistakenly displayed as "dark"; Recheck this internal behavior after changing the library version.
  • 100kHz is conservative for the board, although the chip supports up to 400kHz according to the datasheet.

Project: Light control with hysteresis

Connect a suitable relay or MOSFET to the output with necessary isolation and protection. Turn the light on only when several consecutive samples are below the lower threshold and turn off only after several samples pass the higher threshold. The distance between two thresholds is the same as hysteresis and prevents rapid disconnection near the border.

Optical window and calibration

If the sensor is placed behind the glass, matte frame or product cover, part of the light will be reduced. The datasheet allows to compensate for the window effect by changing the Measurement Time Register, but the coefficient cannot be guessed. A reference lux meter, fixed geometry, stable source and multiple illumination levels are required.

The datasheet accuracy table gives a "raw output to true lux" ratio of 0.96 to 1.44 with a typical value of 1.2 at 1000lx white LED light and reference conditions of VCC=DVI=3V and 25°C. So dividing by 1.2 in High mode and MTreg=69 shows the nominal deviation of about ±20%; This number does not guarantee the same accuracy in every light and environment. At the same setting, the calculated ceiling of 65535/1.2 is about 54613lx and is halved at High2. The code will warn of the full-scale value and not print as a valid measurement.

Common errors and troubleshooting

signprobable causeSuggested review
Sensor not foundWrong address or base, no shared landRun the scanner and check for 0x23/0x5C.
Fixed or old valueRead before the end of the conversionUse hasValue() and start the next instance.
Jump near the thresholdShadow, direct light or single-threshold logicAdd multi-sample median and hysteresis.
The difference with the lux meterSensor tolerance, angle or optical coverageCalibrate in fixed geometry with a valid reference.
The module on the Uno gets hot or damagedThe board does not have a regulator/level shifterCheck the module schematic and work with 3.3V and compatible logic.

BH1750 FAQ

What is the default address of the BH1750?

With ADDR low, the seven-bit address is 0x23; Connecting ADDR to VCC changes it to 0x5C.

Can raw BH1750 be connected to 5V?

no The working VCC of the raw chip is 2.4 to 3.6V. Only a ready board that explicitly has a regulator and level shifter may accept 5V.

Does the BH1750 measure light or UV color?

no Its output is illuminance in lux, and color or UV measurement requires another sensor.

Why doesn't the value change immediately after initialization?

High Resolution conversion can take up to 180ms. A non-blocking read prevents the previous value after the data is ready.

Is GPIO21 and GPIO22 correct for all ESP32s?

no These are the common Generic ESP32 defaults; Check your actual board pins, especially S3 and C3.

Resources and the way to continue learning

  1. ROHM BH1750FVI Datasheet, Rev.D, November 2011, pages 1 and 2 for power and specifications, pages 5 and 7 for modes and data conversion; Developer version hosted on Mouser.
  2. Adafruit BH1750 Board Pinouts Official Guide for VIN, Level Shifter and Address.
  3. Official Arduino Guide for BH1750 and library installation.
  4. hp_BH1750 library code and examples version 1.0.2, written by Stefan Armborst with MIT license.
  5. Official I2C on Arduino-ESP32 documentation for basics and API.

Summarizing and testing on the desktop

To start, connect the document board with compatible voltage, find the address with the scanner and run the High measurement with MTreg=69. Then experiment with light and shade changes, sensor front cover, high light, and disconnection on your hardware. These examples are compiled for Uno and Generic ESP32; Compilation does not take the place of actual sensor testing and product calibration.

Question and answer article

Ask your technical question

Questions and answers are published after moderation. Authors can see their own pending drafts.

No confirmed questions have been registered for this article yet.