Measure light in lux with a BH1750 on an Arduino
Beginner
Or see every lesson.
- 465parts on the bench
- 43boards running now
- 1.00xreal time, on every board
The BH1750 is a light sensor that gives you a real number: lux, the unit a light meter uses. A light-dependent resistor gives a reading that depends on the resistor beside it and the batch it came from. The BH1750 is filtered to match the human eye and reports lux over I2C, with nothing for you to calibrate. The common module is the GY-302.
The circuit above is an Uno with a BH1750 on its I2C pins and an LED on pin 9 that gets brighter as the room gets darker. Drag the slider over the sensor to change the light, and the serial monitor prints the lux and the lamp’s brightness five times a second. It runs in your browser on the sketch below.
What you need
- An Arduino Uno
- A BH1750 module (GY-302)
- An LED and a 220 ohm resistor
- A breadboard and jumper wires
Wiring
| Part and pin | Goes to | Note |
|---|---|---|
| BH1750 VCC | 5 V | The module has its own 3.3 V regulator |
| BH1750 GND | GND | |
| BH1750 SDA | SDA (A4) | The module carries the pull-ups |
| BH1750 SCL | SCL (A5) | |
| BH1750 ADDR | Not connected | Address 0x23; high makes it 0x5C |
| LED anode, through 220 ohm | Pin 9 | A PWM pin, so it can fade |
What a lux reading means
A dark room is a few lux. A classroom is a few hundred, an overcast day outdoors a few thousand, and direct sun around a hundred thousand. The slider over the sensor is logarithmic for that reason: the middle is a classroom and the right end is the sun.
The chip counts light at 1.2 counts per lux and stops at 65535 counts. Divide and that is 54612 lux, the most it can report. Drag the slider to the far right and the reading sits at "light 54612 lx", as a real one does in full sun.
The sketch, step by step
BH1750 lightMeter makes the sensor object, and lightMeter.begin() starts it in its default mode: a new reading every 120 milliseconds at 1 lux resolution. begin() returns false if the sensor does not answer, and the sketch says so.
In loop(), measurementReady() says whether a new reading is in. readLightLevel() gives the lux as a float, which is what most tutorials print. This sketch does sums with the reading, and the Uno has no floating-point hardware, so it uses readLightLevelCentis() instead: the same reading in hundredths of a lux, as a whole number, divided by 100.
Then the lamp. Under 20 lux it is full on, 255. Over 500 it is off. In between it fades: brightness = 255 x (500 - lux) / 480. At the start the light is 300 lux and the line reads "light 300 lx lamp 106". At 100 lux it is 212, and at 20 lux it is 255.
#include <Wire.h>
#include <BH1750.h>
const int LAMP = 9;
const long DARK = 20;
const long BRIGHT = 500;
BH1750 lightMeter;
void setup() {
Serial.begin(115200);
Wire.begin();
if (!lightMeter.begin()) {
Serial.println("no BH1750: check SDA, SCL and power");
}
pinMode(LAMP, OUTPUT);
}
void loop() {
if (lightMeter.measurementReady()) {
long lux = lightMeter.readLightLevelCentis() / 100;
int level;
if (lux <= DARK) {
level = 255;
} else if (lux >= BRIGHT) {
level = 0;
} else {
level = (int)(255 * (BRIGHT - lux) / (BRIGHT - DARK));
}
analogWrite(LAMP, level);
Serial.print("light ");
Serial.print(lux);
Serial.print(" lx lamp ");
Serial.println(level);
}
delay(200);
}Modes and two sensors on one bus
begin() takes a mode. CONTINUOUS_HIGH_RES_MODE, the default, measures over and over at 1 lux. CONTINUOUS_LOW_RES_MODE measures in 16 ms at 4 lux steps. The ONE_TIME modes take a single reading and power the chip down, which saves battery in a logger that wakes once a minute.
The ADDR pin picks the address: 0x23 when it is low or open, 0x5C when it is high. Tie one sensor’s ADDR to 3.3 V and you can read two on the same two wires, one pointed at the sky and one at the room.
Common mistakes
Not checking the reading. With SDA disconnected this sketch prints "no BH1750: check SDA, SCL and power" and then "light -1 lx", because the library’s readings go negative when the sensor does not answer, and the lamp goes full on. Treat a negative reading as an error, not as darkness.
Reading before the first measurement is done. Until the first 120 ms have passed the reading is 0.
Two sensors with ADDR the same. They both answer at 0x23. Tie one high.
Expecting it to see sunlight through a window at full scale. It stops at 54612 lux, and direct sun is more than that.
The wrong pins for I2C. On an Uno SDA is A4 and SCL is A5; the SDA and SCL pins by the USB socket are the same two lines.
Questions
How do I connect a BH1750 to an Arduino Uno?
VCC to 5 V, GND to GND, SDA to A4 and SCL to A5. Leave ADDR open for address 0x23. The GY-302 module has a regulator and pull-ups, so nothing else is needed.
BH1750 or LDR: which should I use?
An LDR is cheaper and fine for "is it dark". The BH1750 gives lux you can compare with a light meter or a datasheet, with no calibration, so use it when the number matters.
Which BH1750 library does this use?
BH1750 by Christopher Laws, the one the Library Manager finds first. Mokxi includes it, so #include <BH1750.h> works without installing anything.
What is the maximum lux a BH1750 can read?
In its default mode, 54612 lux: 65535 counts divided by 1.2. Raising the measurement time with setMTreg() reaches further into the dark and saturates sooner.
Keep going
Build this for real
Open the editor, change a value and watch the number move with it. Nothing to install, and no account needed.