Overview
The BH1750 is a digital ambient light sensor that measures light intensity in lux units. Unlike analog sensors, it uses I2C communication to provide precise, calibrated readings directly to your Arduino without additional calculations.
On the Vumbua Synapse Kit, the BH1750 is pre-connected to the I2C bus at address 0x5C, ready for you to start measuring light levels instantly.
• High precision: 1-65535 lux measurement range
• Low power consumption: 0.12mA in active mode
• I2C digital interface (no ADC needed)
• Built-in 16-bit AD converter
• Smallest measurement resolution: 1 lux
• Wide spectral response: Close to human eye response
Live Light Intensity
Prerequisites
Before starting, make sure you have completed:
Completed Lessons
- Lesson 1: Blinking LED (Basic Output)
- Lesson 2: Push Button Input (Digital Input)
- Lesson 3: Buzzer Sound Generation
- Lesson 4: Potentiometer (Analog Input)
- Understanding of Serial Monitor
Hardware Requirements
- Vumbua Synapse Kit with BH1750 sensor
- USB Type-C cable
- Computer with Arduino IDE
- Various light sources for testing
The BH1750 is pre-connected to the I2C bus on the Vumbua board. I2C pins on ATmega328P: SDA (A4) and SCL (A5). Address is set to 0x5C via ADDR pin pull-up.
Learning Objectives
By the end of this lesson, you will be able to:
- Understand I2C communication protocol fundamentals
- Use the BH1750 library for light sensing
- Read ambient light intensity in lux units
- Configure different measurement modes
- Create smart lighting automation systems
- Calibrate sensor for different environments
- Display light data on Serial Monitor
- Implement daylight harvesting systems
- Understand ATmega328P TWI (I2C) implementation
What is BH1750?
Digital Ambient Light Sensor
The BH1750 is a digital light sensor that converts light intensity directly to digital data using an integrated 16-bit analog-to-digital converter. It outputs values in lux - the SI unit of illuminance.
Lux represents the amount of light that falls on a surface. One lux equals one lumen per square meter.
How It Works
Internally, the BH1750 consists of:
- Photodiode: Converts light to electrical current
- Current-to-Voltage Converter: Amplifies the signal
- 16-bit ADC: Digitizes the analog signal
- I2C Interface: Communicates with microcontroller
- Digital Signal Processor: Processes the data
Technical Specifications
| Supply Voltage | 2.4V - 3.6V (3.3V on Vumbua) |
| I2C Voltage | 1.8V - 3.6V |
| Measurement Range | 1 - 65535 lux |
| Resolution | 1 lux (at minimum) |
| Accuracy | ±20% (typical) |
| Operating Current | 120μA (typical) |
| Interface | I2C-bus (Fast Mode 400kHz) |
Human Eye Response
Spectral response close to human eye sensitivity
Built-in 16-bit ADC
High-resolution digital conversion
Low Power
0.12mA active, 1μA power-down
I2C Interface
Standard 2-wire communication
Understanding I2C Protocol
I2C (Inter-Integrated Circuit) is a serial communication protocol that allows multiple devices to communicate using just two wires. It's essential for communicating with the BH1750 sensor.
I2C Communication Basics
I2C Wires
SDA (Serial Data): Bidirectional data line. Carries data between devices.
SCL (Serial Clock): Clock signal generated by master. Synchronizes data transfer.
Communication Flow
- Master initiates START condition
- Master sends slave address (0x5C)
- Slave acknowledges with ACK
- Data transfer occurs
- Master sends STOP condition
I2C lines require pull-up resistors (typically 4.7kΩ) to keep them HIGH when idle. On Vumbua Synapse Kit, these resistors are already included on the board, so you don't need to add them externally.
BH1750 Pin Connections on Vumbua
The BH1750 module has 5 pins, but only 4 are used on Vumbua Synapse Kit:
The BH1750 operates at 3.3V, not 5V! The Vumbua board includes a 3.3V regulator and level shifting for safe operation. Never connect BH1750 directly to 5V pins.
I2C Address: 0x5C
Each I2C device has a unique address. The BH1750 can have two addresses depending on the ADDR pin:
Address Configuration
- ADDR = LOW (0V): Address = 0x23
- ADDR = HIGH (3.3V): Address = 0x5C
On Vumbua Synapse Kit, ADDR is pulled HIGH via resistor, so the address is 0x5C.
I2C Address Format
I2C addresses are 7-bit values (0-127). The BH1750 address 0x5C in binary is:
When sending the address on I2C, it's shifted left by 1 bit, and the LSB indicates read/write mode (0=write, 1=read).
Using BH1750 Library
The easiest way to use BH1750 is with the dedicated Arduino library:
1. Install BH1750 Library
In Arduino IDE:
- Go to Tools → Manage Libraries...
- Search for "BH1750"
- Install "BH1750" by Christopher Laws
- Alternatively, install "BH1750FVI"
2. Library Functions
Key functions provided by the library:
BH1750.begin(mode, addr)- Initialize sensorBH1750.readLightLevel()- Read light in luxBH1750.configure(mode)- Change measurement mode
3. Include Library
Start your sketch with:
#include <Wire.h> #include <BH1750.h> BH1750 lightMeter;
I2C Scanner Code
Use this code to scan for I2C devices and verify BH1750 is detected at 0x5C:
i2c_scanner.ino
/* I2C Scanner for Vumbua Synapse Kit Scan I2C bus and detect connected devices */ #include <Wire.h> void setup() { Wire.begin(); Serial.begin(9600); Serial.println("\nI2C Scanner for Vumbua Synapse Kit"); Serial.println("================================="); } void loop() { byte error, address; int nDevices = 0; Serial.println("Scanning I2C bus..."); for (address = 1; address < 127; address++) { Wire.beginTransmission(address); error = Wire.endTransmission(); if (error == 0) { Serial.print("I2C device found at address 0x"); if (address < 16) { Serial.print("0"); } Serial.print(address, HEX); Serial.println("!"); // Check if it's BH1750 if (address == 0x23) { Serial.println(" → BH1750 (ADDR = LOW)"); } else if (address == 0x5C) { Serial.println(" → BH1750 (ADDR = HIGH) ✓ Vumbua Kit"); } nDevices++; } } if (nDevices == 0) { Serial.println("No I2C devices found. Check connections!"); } else { Serial.println("Scan complete."); } Serial.println(); delay(5000); }
Basic Light Reading
Let's start with a simple program to read and display light intensity:
bh1750_basic.ino
/* Vumbua Synapse Kit - Lesson 5: Basic BH1750 Light Sensor Read ambient light intensity in lux units BH1750 I2C address: 0x5C */ #include <Wire.h> #include <BH1750.h> // Create BH1750 object with address 0x5C BH1750 lightMeter(0x5C); void setup() { Serial.begin(9600); // Initialize I2C bus (Wire) Wire.begin(); // Initialize BH1750 sensor if (lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE)) { Serial.println("BH1750 Light Sensor Initialized Successfully!"); } else { Serial.println("Error: BH1750 not detected. Check connections."); while (1); // Stop if sensor not found } Serial.println("Vumbua Synapse Kit - Light Intensity Monitor"); Serial.println("============================================"); Serial.println("Move sensor to different light conditions"); Serial.println(); } void loop() { // Read light level in lux float lux = lightMeter.readLightLevel(); // Determine light condition String condition = getLightCondition(lux); // Display results Serial.print("Light Intensity: "); Serial.print(lux); Serial.print(" lux ("); Serial.print(condition); Serial.println(")"); // Wait 1 second before next reading delay(1000); } // Function to categorize light levels String getLightCondition(float lux) { if (lux < 10) { return "Dark"; } else if (lux < 50) { return "Dim"; } else if (lux < 200) { return "Normal Indoor"; } else if (lux < 1000) { return "Bright Indoor"; } else if (lux < 10000) { return "Daylight"; } else { return "Direct Sunlight"; } }
Testing the Program
1. Upload the Code
Upload to your Vumbua Synapse Kit. The BH1750 is already connected to the I2C bus at address 0x5C.
2. Open Serial Monitor
Go to Tools → Serial Monitor or press Ctrl+Shift+M.
Set the baud rate to 9600.
You should see initialization message and continuous light readings.
3. Test Different Light Conditions
Try these experiments:
- Cover sensor with your hand (should read below 10 lux)
- Normal room lighting (200-500 lux)
- Near a lamp (1000-2000 lux)
- Near window in daylight (5000-10000 lux)
- Direct sunlight (up to 100,000 lux but sensor max is 65535)
Light Level Reference Guide
BH1750 Measurement Modes
The BH1750 supports 6 different measurement modes for various applications:
| Mode | Resolution | Measurement Time | Power Usage | Best For |
|---|---|---|---|---|
| CONTINUOUS_HIGH_RES | 1 lux | 120ms | 120μA | General purpose, indoor |
| CONTINUOUS_HIGH_RES_2 | 0.5 lux | 120ms | 120μA | High precision indoor |
| CONTINUOUS_LOW_RES | 4 lux | 16ms | 120μA | Fast response, bright light |
| ONE_TIME_HIGH_RES | 1 lux | 120ms | 0.1μA* | Battery-powered, sleep mode |
| ONE_TIME_HIGH_RES_2 | 0.5 lux | 120ms | 0.1μA* | Battery, high precision |
| ONE_TIME_LOW_RES | 4 lux | 16ms | 0.1μA* | Battery, fast reading |
* Power-down current between measurements
Mode Selection Example
mode_selection.ino
/* BH1750 Mode Selection Demo Show different measurement modes */ #include <Wire.h> #include <BH1750.h> BH1750 lightMeter(0x5C); void setup() { Serial.begin(9600); Wire.begin(); Serial.println("BH1750 Mode Comparison"); Serial.println("======================"); } void loop() { // Test CONTINUOUS_HIGH_RES mode lightMeter.configure(BH1750::CONTINUOUS_HIGH_RES_MODE); delay(120); // Wait for measurement float luxHighRes = lightMeter.readLightLevel(); // Test CONTINUOUS_LOW_RES mode lightMeter.configure(BH1750::CONTINUOUS_LOW_RES_MODE); delay(16); float luxLowRes = lightMeter.readLightLevel(); // Test ONE_TIME_HIGH_RES mode lightMeter.configure(BH1750::ONE_TIME_HIGH_RES_MODE); delay(120); float luxOneTime = lightMeter.readLightLevel(); // Display comparison Serial.print("HIGH_RES (1 lux): "); Serial.print(luxHighRes); Serial.print(" lux | LOW_RES (4 lux): "); Serial.print(luxLowRes); Serial.print(" lux | ONE_TIME: "); Serial.print(luxOneTime); Serial.println(" lux"); delay(2000); }
Continuous modes are best for real-time monitoring. One-time modes are ideal for battery-powered applications because the sensor goes to sleep between measurements, saving power.
Sensor Calibration & Accuracy
While BH1750 is factory-calibrated, you can improve accuracy for your specific application:
1. Calibration Method
Compare BH1750 readings with a reference lux meter or known light sources:
- Standard candle at 1 meter: ~1 lux
- 60W incandescent bulb at 1 meter: ~100 lux
- Direct sunlight: 10,000-100,000 lux
- Overcast daylight: 1,000-2,000 lux
2. Calibration Factor
Apply a correction factor to your readings:
Typical factory calibration factor is 1.2, but can vary.
3. Averaging for Stability
Take multiple readings and average them to reduce noise:
float getAverageLux(int samples) {
float sum = 0;
for(int i = 0; i < samples; i++) {
sum += lightMeter.readLightLevel();
delay(10);
}
return sum / samples;
}
Calibration Code Example
calibration.ino
/* BH1750 Calibration Example Apply calibration factor for improved accuracy */ #include <Wire.h> #include <BH1750.h> BH1750 lightMeter(0x5C); // Calibration factor - adjust based on reference measurement const float CALIBRATION_FACTOR = 1.2; void setup() { Serial.begin(9600); Wire.begin(); lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE); Serial.println("BH1750 Calibrated Measurement"); Serial.println("Calibration Factor: " + String(CALIBRATION_FACTOR)); } float getStableLux() { // Take 5 readings and average float sum = 0; for (int i = 0; i < 5; i++) { sum += lightMeter.readLightLevel(); delay(50); } return sum / 5.0; } void loop() { // Get raw measurement float rawLux = getStableLux(); // Apply calibration float calibratedLux = rawLux * CALIBRATION_FACTOR; // Display results Serial.print("Raw: "); Serial.print(rawLux); Serial.print(" lux | Calibrated: "); Serial.print(calibratedLux); Serial.println(" lux"); delay(1000); }
Smart Lighting Automation
Now let's create a practical application - automatic LED control based on ambient light:
smart_lighting.ino
/* Vumbua Synapse Kit - Smart Lighting Automation Control LED brightness based on ambient light Automatic night light / daylight harvesting */ #include <Wire.h> #include <BH1750.h> BH1750 lightMeter(0x5C); // LED connected to PWM pin 9 const int ledPin = 9; // Light thresholds (in lux) const int DARK_THRESHOLD = 50; // Below this: LED ON full const int BRIGHT_THRESHOLD = 200; // Above this: LED OFF void setup() { Serial.begin(9600); Wire.begin(); // Initialize sensor lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE); // Initialize LED pin pinMode(ledPin, OUTPUT; Serial.println("Smart Lighting System Active"); Serial.println("============================"); Serial.println("DARK (<50 lux): LED ON"); Serial.println("DIM (50-200 lux): LED proportional"); Serial.println("BRIGHT (>200 lux): LED OFF"); Serial.println(); } void loop() { // Read ambient light level float lux = lightMeter.readLightLevel(); int ledBrightness = 0; if (lux <= DARK_THRESHOLD) { // Dark: LED at full brightness ledBrightness = 255; } else if (lux >= BRIGHT_THRESHOLD) { // Bright: LED off ledBrightness = 0; } else { // Dim: Proportional brightness // Map lux value (50-200) to brightness (255-0) ledBrightness = map(lux, DARK_THRESHOLD, BRIGHT_THRESHOLD, 255, 0); ledBrightness = constrain(ledBrightness, 0, 255); } // Control LED analogWrite(ledPin, ledBrightness); // Calculate LED brightness percentage int ledPercent = map(ledBrightness, 0, 255, 0, 100); // Display status Serial.print("Light: "); Serial.print(lux); Serial.print(" lux | LED: "); Serial.print(ledBrightness); Serial.print(" ("); Serial.print(ledPercent); Serial.println("%)"); delay(500); }
Ambient Light
LED Brightness
Enhance your smart lighting system with these features:
- Hysteresis: Prevent rapid switching at threshold boundaries
- Scheduling: Combine with real-time clock for time-based control
- Remote Control: Add Bluetooth/WiFi for smartphone control
- Energy Monitoring: Track energy savings from automation
- Multi-zone Control: Control different areas independently
Real-World Applications
Home Automation
- Automatic blinds/curtains control
- Smart garden lighting
- Energy-efficient room lighting
- Security light automation
- Wake-up light simulation
Industrial & Commercial
- Daylight harvesting in offices
- Greenhouse light control
- Photography light metering
- Display case lighting
- Warehouse aisle lighting
Automotive
- Automatic headlight control
- Dashboard brightness adjustment
- Tunnel detection for lights
- Rain/light sensor integration
Consumer Electronics
- Smartphone screen brightness
- Laptop display adjustment
- Camera exposure control
- E-reader frontlight control
Project: Plant Light Monitor
plant_light_monitor.ino
/* Plant Light Monitor Monitor if plants are receiving adequate light */ #include <Wire.h> #include <BH1750.h> BH1750 lightMeter(0x5C); // Plant light requirements (lux) const int PLANT_MIN_LIGHT = 1000; // Minimum for healthy growth const int PLANT_MAX_LIGHT = 10000; // Maximum before damage // LED indicators const int LED_TOO_DARK = 2; const int LED_GOOD = 3; const int LED_TOO_BRIGHT = 4; void setup() { Serial.begin(9600); Wire.begin(); lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE); // Setup LED pins pinMode(LED_TOO_DARK, OUTPUT); pinMode(LED_GOOD, OUTPUT); pinMode(LED_TOO_BRIGHT, OUTPUT); Serial.println("Plant Light Monitor Active"); Serial.println("<1000 lux: Too dark (Red LED)"); Serial.println("1000-10000 lux: Good (Green LED)"); Serial.println(">10000 lux: Too bright (Yellow LED)"); } void loop() { float lux = lightMeter.readLightLevel(); // Reset all LEDs digitalWrite(LED_TOO_DARK, LOW); digitalWrite(LED_GOOD, LOW); digitalWrite(LED_TOO_BRIGHT, LOW); // Control LEDs based on light level if (lux < PLANT_MIN_LIGHT) { digitalWrite(LED_TOO_DARK, HIGH); } else if (lux > PLANT_MAX_LIGHT) { digitalWrite(LED_TOO_BRIGHT, HIGH); } else { digitalWrite(LED_GOOD, HIGH); } // Display recommendation Serial.print("Light: "); Serial.print(lux); Serial.print(" lux - "); if (lux < PLANT_MIN_LIGHT) { Serial.println("Move plant to brighter location"); } else if (lux > PLANT_MAX_LIGHT) { Serial.println("Provide some shade for plant"); } else { Serial.println("Perfect light conditions! ✓"); } delay(2000); }
ATmega328P I2C (TWI) Implementation
The ATmega328P implements I2C through its TWI (Two-Wire Interface) hardware module:
TWI Hardware Features
- Dual-purpose pins: PC4 (SDA) and PC5 (SCL)
- Multi-master support: Multiple masters on same bus
- 7-bit addressing: Supports 128 different addresses
- Clock stretching: Slaves can slow down communication
- Interrupt-driven: Can operate without CPU attention
Clock Speed Configuration
I2C clock speed is set by the TWI Bit Rate Register (TWBR):
For 16MHz Arduino with 100kHz I2C: TWBR = 72
Arduino Wire Library
The Arduino Wire.h library abstracts TWI hardware:
Wire.begin()- Initialize I2C as masterWire.beginTransmission(addr)- Start communicationWire.write(data)- Send dataWire.endTransmission()- End transmissionWire.requestFrom(addr, bytes)- Request dataWire.read()- Read received data
Advanced users can access TWI registers directly for maximum control:
- TWBR: Bit Rate Register
- TWCR: Control Register
- TWSR: Status Register
- TWDR: Data Register
- TWAR: Address Register (for slave mode)
Troubleshooting Common Issues
- Solution 1: Run I2C scanner to check address (0x5C)
- Solution 2: Check connections - SDA to A4, SCL to A5
- Solution 3: Verify 3.3V power supply to BH1750
- Solution 4: Add
Wire.begin()before sensor init - Solution 5: Try alternative address 0x23 if ADDR is LOW
- Check: Light sensor is not covered or damaged Test: Shine flashlight directly on sensor
- Verify: Correct I2C address in code (should be 0x5C for Vumbua)
- Consider: Sensor may be in wrong measurement mode
- Try: Reset sensor with power cycle
- Solution 1: Use averaging function (sample 5-10 readings)
- Solution 2: Ensure stable light source
- Solution 3: Avoid fluorescent lights (they flicker at 50/60Hz)
- Solution 4: Check for electrical noise near sensor
- Solution 5: Use HIGH_RES mode instead of LOW_RES for stability
- Calibrate: Use known light source for calibration
- Check: Sensor viewing angle (should face light source)
- Verify: No shadows or reflections affecting sensor
- Consider: Different light colors affect readings
- Test: Compare with smartphone light meter app
Advanced Debugging: Raw I2C Communication
// Manual I2C communication with BH1750
void readBH1750Manual() {
// Power on command
Wire.beginTransmission(0x5C);
Wire.write(0x01); // Power on
Wire.endTransmission();
delay(10);
// Set measurement mode
Wire.beginTransmission(0x5C);
Wire.write(0x10); // Continuously H-Resolution mode
Wire.endTransmission();
delay(120); // Wait for measurement
// Read 2 bytes
Wire.requestFrom(0x5C, 2);
if(Wire.available() == 2) {
uint16_t raw = Wire.read();
raw <<= 8;
raw |= Wire.read();
float lux = raw / 1.2;
Serial.print("Raw I2C: ");
Serial.print(lux);
Serial.println(" lux");
}
}
Video Demonstration
Light Sensor Video Tutorial
Watch our comprehensive video guide on using the BH1750 light sensor.
Learn setup, calibration, and advanced applications with live demonstrations.
Watch on OLSPACE YouTubeWhat's Next?
Congratulations! You've successfully mastered digital light sensing with BH1750. Here's what you've accomplished:
- ✓ Learned I2C communication protocol fundamentals
- ✓ Used BH1750 library for precise light measurement
- ✓ Mastered different measurement modes for various applications
- ✓ Created smart lighting automation systems
- ✓ Implemented sensor calibration for accuracy
- ✓ Built practical projects like plant light monitor
- ✓ Understood ATmega328P TWI (I2C) implementation
In the next lesson, you'll explore environmental sensing:
Lesson 6: Temperature & Humidity (DHT20)
Learn to measure temperature and humidity using the DHT20 sensor with I2C communication. Create weather stations, climate control systems, and environmental monitoring projects that track both temperature and relative humidity simultaneously.
Resources & Downloads
Download Complete Code
Get all the code files for this lesson: