
BH1750 Light Sensor Library for Proteus V1.1 (GY-302 + Arduino)

Hello friends, I hope you all are doing great. Today, I am going to share the new BH1750 Light Sensor Library for Proteus V1.1. The BH1750 is ROHM's small digital light sensor, best known on the blue GY-302 module: it measures how much light falls on it and gives your Arduino the result in lux over I2C. With this BH1750 Light Sensor Library for Proteus, you set the light with a slider or a light scene, and your Arduino reads it with the same claws BH1750 library you use on real hardware.
Version 1.1 replaces the first version of this post. You now get two devices: BH1750 Simple, with a light panel on the schematic, and BH1750 Advance, with a pop-up window and four measured light test tools. The model now takes the real measurement time and averages the light over every measurement, like the real sensor. The new BH1750 Arduino Proteus demo is a night lamp on two Arduino UNOs, one sensor at each I2C address, shown in the compact Simple interface of our TEP Serial Monitor. Have you read version 1.0? Then jump to "What's New" below.
NOTICE: This library is very special to our team. Our BH1750 model measures with the real timing, averages the light over every measurement like the real photodiode and logs every I2C transaction your sketch sends. It took our team a lot of hard work, many test runs and many design changes. Your feedback is the fuel that keeps us going, so please tell us what you think in the comments below or in the BH1750 Library board of our forum. And if our free libraries help you, you can buy us a coffee. So, let's get started with the BH1750 Light Sensor Library for Proteus:
What is the BH1750 Light Sensor?
The BH1750FVI, made by ROHM, is a digital ambient light sensor. It measures illuminance, the light arriving at a surface, in lux (lx). A photodiode collects the light, the chip turns it into a 16-bit number, and your Arduino reads that number over I2C, a 2-wire bus with a clock line (SCL) and a data line (SDA). You can build a light-controlled lamp, a brightness display or a light logger with it.
This is different from an LDR (light-dependent resistor) on an analog pin. An LDR gives a voltage that your sketch must interpret; the BH1750 hands over a finished measurement, and the library turns it into lux. The common board is the blue GY-302: the BH1750 with its optical window in the middle, a 662K 3.3 V regulator, two 4.7 k pull-up resistors and a 5-pin header, VCC GND SCL SDA ADDR.
The BH1750 at a Glance
| Feature | Value |
|---|---|
| Interface | I2C at address 0x23 (ADDR LOW or open) or 0x5C (ADDR HIGH) |
| Modes | Continuous or one-time, each in H-resolution, H-resolution2 or L-resolution |
| Measurement time | 120 ms in H-resolution and H-resolution2, 16 ms in L-resolution (typical; the data sheet's maximum is 180 / 24 ms), times MTreg / 69 |
| Result | A 16-bit count, 0 - 65535, read as 2 bytes, high byte first |
| Sensitivity | 1.2 counts per lux typical (0.96 - 1.44) at MTreg 69; MTreg 31 - 254 |
| Resolution at MTreg 69 | 0.83 lx (H-resolution), 0.42 lx (H-resolution2), 3.33 lx (L-resolution) |
| Range at MTreg 69 | 0 - 54612 lx (H-resolution), 0 - 27306 lx (H-resolution2) |
| Supply (GY-302) | 3.3 V or 5 V: the module has its own 3.3 V regulator |
Light Levels: The Five Scenes of Our Panels
Our panels have five light scenes, and the board's sky shows a picture for each range:
| Scene | Light | Counts at MTreg 69 | The sky on the board |
|---|---|---|---|
| NIGHT | 1 lx | 1 | A moon (below 10 lx) |
| ROOM | 150 lx | 180 | A lamp (below 1000 lx) |
| OFFICE | 500 lx | 600 | A lamp |
| OVERCAST | 2000 lx | 2400 | A sun behind a cloud (below 10 klx) |
| SUN | 30000 lx | 36000 | The sun |
Light Sensor Terms You Will See in This Article
| Term | Meaning |
|---|---|
| Illuminance, lux (lx) | The light falling on a surface, and its unit; 1 klx = 1000 lx |
| Count | One step of the sensor's 16-bit result; at MTreg 69, 1.2 counts make 1 lx |
| Data register | The chip's 2-byte result; it holds the last finished measurement |
| Measurement time | How long the photodiode collects light for one result |
| MTreg | The measurement time register, 31 - 254 (default 69): higher = slower, more sensitive, finer steps |
| H-resolution, H-resolution2, L-resolution | The three resolutions: the sketch calls them 1, 0.5 and 4 lx steps; with 1.2 counts per lux, one step is 0.83, 0.42 and 3.33 lx |
| Continuous, one-time | Measure again and again, or measure once and then go to Power Down |
| Power Down | The chip's sleep state: no measuring, the data register is kept |
| Saturation | The count stops at 65535, the brightest light the mode and MTreg can show |
| In-between reading | A result measured while the light changed, between the old and the new value |
| ACK, NACK | The I2C "got it" bit after each byte; no ACK means no device answers at that address |
| Pull-up resistor | Keeps SDA and SCL HIGH; the devices only pull the lines LOW (open drain) |
How the BH1750 Measures Light
Opcodes: How the Arduino Talks to the BH1750
The BH1750 is simple to talk to: the Arduino writes one-byte opcodes (instructions) to it and reads two bytes back. The claws BH1750 library names them in its header:
| Opcode | Meaning |
|---|---|
| 00 / 01 | Power Down / Power On |
| 07 | Reset (ignored in Power Down) |
| 10 / 11 / 13 | Continuously H-resolution / H-resolution2 / L-resolution |
| 20 / 21 / 23 | One Time H-resolution / H-resolution2 / L-resolution |
| 4x and 6x | MTreg: its 3 high bits in 01000xxx (4x), its 5 low bits in 011xxxxx (6x) |
So MTreg 69 (binary 010 00101) travels as 42 and 65. A read returns the data register, high byte first: 02 58 means 2 x 256 + 88 = 600 counts.
From Counts to Lux
At MTreg 69 in H-resolution, the sensor makes 1.2 counts per lux, and the library divides by 1.2: 600 counts are 500.0 lx, and 36000 counts are 30000.0 lx. One count is 0.83 lx, so 1 lx on the sensor reads 0.8 lx. H-resolution2 makes twice the counts and the library divides by 2 as well, so it has half the step but saturates at half the light, 27306.2 lx. L-resolution counts in steps of 4.
Measurement Time and MTreg
MTreg sets how long the photodiode collects light: a higher MTreg gives more counts per lux and finer steps, but it is slower and saturates earlier. The library divides by MTreg / 69 too, so it prints the same lux at any MTreg:
| MTreg | Time (H-resolution) | Step | Full scale |
|---|---|---|---|
| 31 | 54 ms | 1.85 lx | 121557 lx |
| 69 (default) | 120 ms | 0.83 lx | 54612 lx |
| 138 | 240 ms | 0.42 lx | 27306 lx |
| 254 | 442 ms (663 ms maximum) | 0.23 lx | 14836 lx |
Continuous or One-Time?
In a continuous mode, the sensor starts the next measurement as soon as one ends, so the data register always holds a recent result. In a one-time mode, it makes one measurement and goes to Power Down: the sketch must send the mode again, wait the measurement time and then read. A read before the first measurement has finished gives 0 lx, on the real chip too.
The Light Is Averaged over Each Measurement
The photodiode collects light for the whole measurement, so the result is the average light of those 120 ms. If the light changes in the middle, one result lands between the old and the new value. Version 1.1 models this; v1.0 used the light at the moment of reading. You will meet these in-between readings in the panels, on the monitor and in the Step response tool.
The ADDR Pin: Two Sensors on One Bus
ADDR LOW or open selects 0x23 (the module has a pull-down), ADDR HIGH selects 0x5C. So two BH1750s can share one I2C bus, one at each address; a transaction to the other address gets no ACK. Our demo uses both: U1 has ADDR to GND, U2 has ADDR to VCC.
What's New in BH1750 Light Sensor Library for Proteus V1.1
If you used version 1.0, here is what has changed:
- Two devices in
TEPBH1750.LIB: BH1750 Simple (BH1750TEP, the v1.0 device, so old designs keep working) and BH1750 Advance (BH1750ADVTEP) with a pop-up window. - A redrawn board: the blue GY-302 instead of v1.0's square board, with a sky that shows the light, a glowing sensor window, a MEAS bar, a live ADDR box and PWR / I2C LEDs (a TEP addition).
- Real measurement timing: 120 ms in H-resolution, 16 ms in L-resolution, times MTreg / 69, with MTreg changing the counts per lux; one-time modes go to Power Down after their measurement.
- The light is averaged over each measurement, so a change gives one in-between reading.
- The ADDR pin (0x23 or 0x5C, shown on the board) and an I2C transaction log with the opcodes decoded and the SCL clock measured.
- A log slider up to 100 klx (v1.0: 0 - 2000 lx, linear), five light scenes, a live chart and the new ACCURACY and TIMING properties.
- Four measured test tools, Settings and Help on the Advance.
- A two-UNO demo with the TEP Serial Monitor instead of the Virtual Terminal, with a night lamp on A0 and Serial Monitor commands.
- Fixes: Simulation Log lines (v1.0 wrote none), and the panel no longer changes the light while the simulator reads it.
- Lighter package: about 1.45 MB, without the C++ source code (v1.0: about 7.6 MB).
BH1750 Light Sensor Library for Proteus: Simple vs Advance
Both devices run the same model with the same properties; only the panel differs:
| Feature | Simple | Advance |
|---|---|---|
| Full BH1750 model (0x23 / 0x5C, six modes, MTreg, real timing, light averaged over each measurement) | ✔ | ✔ |
| Blue GY-302 board with the sky, glowing window, MEAS bar, ADDR box and PWR / I2C LEDs | ✔ | ✔ |
| Light panel on the schematic (log slider, five scenes, 5 s chart, last I2C transactions) | ✔ | ✘ |
| Pop-up window you can move, resize and minimise | ✘ | ✔ |
| Light page: Dark and five scenes, Darker / Brighter, THE SENSOR card, 10 s chart | ✘ | ✔ |
| I2C check, Timing, Step response and Lux sweep | ✘ | ✔ |
| TEP Dark / Light theme, text size, Help page with Copy diagnostics | ✘ | ✔ |
| Same device name as v1.0 (old designs keep working) | ✔ | ✘ |
Choose Simple to keep the light control beside your circuit, and Advance when your sheet is full or you want hard numbers. The demo has one of each.
Download BH1750 Light Sensor Library for Proteus
Click the button below to download BH1750-Light-Sensor-Library-for-Proteus-v1.1.zip (about 1.45 MB, without the C++ source code):
BH1750 Light Sensor Library for Proteus V1.1- README.txt: the full guide, from the wiring and the test tools to real hardware.
- Proteus Library Files:
TEPBH1750.LIB(both devices),TEPSERIALMON.LIBand our Arduino UNO libraryArduinoV3TEP.LIB/ArduinoV3TEP.IDX. - Proteus Model Files:
TEPBH1750.DLLandTEPSERIALMON.DLL. - Proteus Simulation:
BH1750-ArduinoUnoV3.pdsprj,BH1750_Demo.hexand copies of both DLLs. - Arduino Code:
BH1750_Demo.inoand the BH1750 library 1.3.0 by Christopher Laws (claws), unmodified, with its examples, under the MIT licence (LICENSE).
No Proteus yet? Read How to Download and Install Proteus 7 and 8. New to libraries? Follow How to Install a New Library in Proteus.
How to Install BH1750 Light Sensor Library for Proteus
- Close Proteus and extract the whole zip file.
- Copy the four files from Proteus Library Files into the LIBRARY folder, usually
C:\Program Files (x86)\Labcenter Electronics\Proteus 8 Professional\LIBRARY(on some PCsC:\ProgramData\Labcenter Electronics\Proteus 8 Professional\LIBRARY). - Copy
TEPBH1750.DLLandTEPSERIALMON.DLLinto the MODELS folder. - Start Proteus, press P and search for BH1750. You get the Simple and the Advance device (category Sensors > Light, as in v1.0).
Updating from v1.0? Replace TEPBH1750.LIB and TEPBH1750.DLL, and delete TEPBH1750.IDX if there is one in LIBRARY. A v1.0 design keeps its v1.0 symbol: it runs with the new model and shows the v1.0 slider panel exactly as before, without the v1.1 board parts; re-pick the part to get the v1.1 board. Tested in Proteus 8.5; Proteus 7 is not supported.
The BH1750 (GY-302) Board in Proteus
We designed the sensor as a clean TEP board in the blue and the tall shape of the GY-302: the BH1750 with its optical window in the middle, the 662K regulator and capacitors on the left, the two 472 (4.7 k) pull-ups on the right and two gold mounting holes. Above the board, a round sky with 12 rays sends three beams down to the window. Here is U1, the Simple device, a moment after I clicked OFFICE:
The sky shows a lamp, the rays are lit two segments of three, and the window glows. The green I2C LED flashes as the Arduino reads, the cyan MEAS bar fills during each 120 ms measurement, and the ADDR box says ADDR 0x23.
Pinout
| Pin | Function | Demo connection |
|---|---|---|
| VCC | Power (3.3 V or 5 V on a real GY-302) | Supply terminal |
| GND | Ground | GND |
| SCL | I2C clock from the Arduino | Arduino A5 |
| SDA | I2C data, both ways (open drain) | Arduino A4 |
| ADDR | Address select: LOW or open = 0x23, HIGH = 0x5C | U1: GND; U2: VCC |
Sky, Window and LEDs
| Indicator | What it shows |
|---|---|
| Sky | DARK at 0 lx, a moon below 10 lx, a lamp below 1000 lx, a sun behind a cloud below 10 klx, then the sun |
| 12 rays and 3 beams | 0, 1, 2 or 3 segments of every ray light up as the light grows; the beams run down to the window |
| Sensor window and halo | Glow from dim amber to white-yellow with the light; sparkle rays from about 200 lx |
| MEAS bar | Fills during every measurement: cyan in H-resolution, violet in H-resolution2, amber in L-resolution |
| ADDR box | The address the ADDR pin selects, 0x23 or 0x5C |
| PWR LED (red) | Lit while VCC and GND are powered |
| I2C LED (green) | Lights for 0.3 s (simulation time) after every transaction to this sensor |
| PANEL / OPEN PANEL, SIMPLE / ADVANCE | The panel button and the device badge |
Both LEDs are a TEP addition: the real GY-302 has no LED.
Component Properties
| Property | Meaning | Default |
|---|---|---|
| LUX | The light at the start of a run, lx | 100 |
| LUX2 | The light after the timed step, lx | 1000 |
| STEP_TIME | The step time in seconds, 0 = no step | 0 |
| LUX_MAX | The slider's maximum, lx (v1.0: 2000 on a linear slider; a v1.0 design keeps its value) | 100000 |
| TIMING | TYP (120 / 16 ms) or MAX (the data sheet's 180 / 24 ms) | TYP |
| ACCURACY | Counts per lux at MTreg 69, 0.96 - 1.44 (the library always divides by 1.2) | 1.2 |
LUX, LUX2 and STEP_TIME work as in v1.0: with LUX 100, LUX2 1000 and STEP_TIME 3, the light steps to 1000 lx at 3 s. A slider or scene click overrides the step for the rest of the run, and every run starts again from LUX. Set ACCURACY to 1.44 to try a sensor that reads 20 % high.
BH1750 Simple: The Light Panel on the Sheet
Beside the Simple board sits the BH1750 LIGHT CONTROL panel (on the right of the image above):
| Part | What it shows |
|---|---|
| Header, red X | BH1750 LIGHT CONTROL; the X closes the panel |
| State bar | CONTINUOUS H-RES - 120 MS, ONE-TIME ..., POWER DOWN - WAITING FOR THE SKETCH or NO POWER - CHECK VCC / GND |
| LIGHT ON THE SENSOR / THE SENSOR READS | The light you set and the last result, with its counts, mode, MTreg and address |
| Log slider | Drag or click: 0 lx at the left stop, then 0.1 lx to 100 klx |
| Five scenes | NIGHT 1, ROOM 150, OFFICE 500, OVERCAST 2000 and SUN 30000 lx |
| Chart, last 5 s | The light as an orange line, every measurement as a blue dot, an in-between one in violet |
| LAST I2C TRANSACTIONS | The reads decoded, for example R 23: 02 58 = 600 COUNTS |
From OFFICE to SUN
In the image above, the chart shows the step from 100 to 500 lx and, right at the step, one violet dot: the measurement that was running when I clicked. Now click SUN:
The sky turns into the sun, every ray is lit, and the window shines white-yellow. The sensor makes 36000 counts (8C A0 in hex), and the library prints 30000.0 lx. That is still below the 65535 limit; full sunlight up to 100 klx would saturate the sensor at 54612.5 lx.
NIGHT: The Night Lamp Lights
Click NIGHT (1 lx):
The sky shows a moon, and the sensor makes just 1 count. One count is 0.83 lx, so 1.0 lx reads 0.8 lx, a 17 % error that only matters in the dark. That is below the sketch's 50 lx threshold, so UNO 1 switches on the night lamp, the green LED D1 on A0. The image also shows the wiring of NODE 1: SCL to A5, SDA to A4 and ADDR to ground.
Closing the Panel
Need room on the sheet? Click the red X: the board button says PANEL: CLOSED with a grey dot, and the sensor keeps measuring. PANEL brings it back.
BH1750 Advance: The Pop-Up Light Panel
The Advance device keeps only the board on the schematic. Here is U2:
ADDR goes to a power terminal, so the box says ADDR 0x5C. At Run, the "TEP BH1750 Advance" pop-up opens with the look of our Serial Monitor and no Windows title bar: move it by its header, resize it, minimise it, and reopen it with OPEN PANEL. Its header shows the state (for example U2 · CONT H-RES, POWER DOWN or TESTING), the Light and Test tabs and the theme, Settings and Help icons.
The Light Page
LIGHT ON THE SENSOR has the value, the log slider, Dark and the five scenes, and Darker x 0.5 / Brighter x 2. THE SENSOR shows what the chip is doing: power, mode, the measurement time with a progress bar, MTreg, the address and ADDR pin, the data register (600 counts = 500.0 lx), the resolution and range, and the measurements and I2C LED flashes so far.
LIVE - THE LAST 10 SECONDS draws the light, every measurement, the in-between ones and the sketch's reads as green rings. The I2C list tells the story of my click on Office 500: the sketch read 120 counts (100 lx) every 500 ms, then 586 counts, an in-between measurement that saw 500 lx for most of its 120 ms (the library makes 488.3 lx of it), and then 600 counts.
The Test Page: Four Measured Light Test Tools
The four tools on the Test tab only set the light and read the sensor's own logs: the finished measurements and the I2C transactions. They never talk to your Arduino, so every number is what your sketch and the sensor really did, measured with the simulation clock. Each tool ends with a "Good for:" line. All results below come from U2.
1. I2C Check: What Does Your Sketch Send?
This tool is live: it decodes the SDA and SCL pins while the sketch runs.
The top row says 0x5C ADDR HIGH, an SCL clock of 93.1 kHz measured from its rising edges (Wire is set to 100 kHz), 174 transactions to this sensor and 1 to another address. WHAT THE SKETCH SENT counts 2 mode writes, 2 MTreg bytes, 168 reads and 1 address probe.
THE SKETCH'S SET-UP lists the first transactions after power-up. At 65.4 ms, the sketch tried 0x23 and got no ACK, because U2 sits at 0x5C. Then its probe of 0x5C got an ACK, and begin() sent 10, about 10 ms later 42 and 65 (MTreg 69) and 10 again. The first read, at 428.1 ms, got 00 78 = 120 counts, that is 100 lx. Below, the last 16 transactions: 600 counts every 500 ms. Good for: checking which commands your sketch really sends, to which address, and how fast the clock is.
2. Timing: Does Your Sketch Read Fresh Results?
Choose a capture of 2, 5 or 10 s and click Run test. Here is 5 s with the demo's continuous H-resolution:
The sensor finished 42 measurements of exactly 120.0 ms (8.3 per second), and the sketch read 10 times, every 500 ms. All 10 reads were fresh, a measurement the sketch had not read before. None was the same value again (a sketch that reads faster than the sensor measures) and none came too early. The value read was 55 ms old on average (13 - 113 ms): 500 ms is not a multiple of 120 ms, so each read lands at another point of the measurement cycle. The timeline shows the first 2 s, and the table lists the three resolutions at the MTreg in use.
Now click ONCE and MTREG 254 on U4 and capture again:
The header now says U2 · ONCE H-RES. One measurement takes 441.7 ms (120 x 254 / 69), and the sketch read 8 times, every 674 ms, all fresh and never too early. Before each read, the sketch sends the mode (a violet line), the library waits 10 ms, and the sketch waits 663 ms, the data sheet's maximum at MTreg 254. So every value is 231 ms old (673 - 441.7 ms), and the interval grows from 500 to 674 ms, because the sketch waits inside its reading. Between measurements, the chip sits in Power Down. Good for: checking that your sketch waits for a fresh result, and seeing how old the value it reads is.
3. Step Response: How Fast Does a Change Reach Your Sketch?
Choose a step (100 to 1000, 1000 to 100, 10 to 10k or 0 to 500 lx) and 3, 5 or 10 runs; each run steps at a different moment against the measurements. 100 to 1000 lx, five runs:
| Run | In-between | Full result | Sketch has the new light |
|---|---|---|---|
| 1 | 52 ms: 486.7 lx | 172 ms | 485 ms |
| 2 | 85 ms: 734.2 lx | 205 ms | 438 ms |
| 3 | 117 ms: 974.2 lx | 237 ms | 390 ms |
| 4 | 24 ms: 276.7 lx | 144 ms | 456 ms |
| 5 | 56 ms: 516.7 lx | 176 ms | 409 ms |
In all 5 runs, the measurement running at the step ended with an in-between value, 66 ms after the step on average. Its value depends on how much of its 120 ms was left: in run 3, 117 ms of it saw 1000 lx, and it read 974.2 lx; in run 4, only the last 24 ms did, and it read 276.7 lx. The next measurement, the first full result, ended 120 ms later, 186 ms after the step on average. The sketch saw the change after 436 ms on average (390 - 485 ms), because it reads only every 500 ms, and here its first changed read was already the full 1000.0 lx. Good for: seeing how long a change of light takes to reach your sketch: the measurement plus its read interval.
4. Lux Sweep: Resolution, Range and Saturation
Choose Dim 0 - 10 lx, Indoor 0 - 1000 lx or Full 0.1 lx - 100 klx (log). At every point, the tool waits until the sketch has read a full measurement made at that light (3 s at most). Full, 14 points:
At 0.1 and 0.3 lx, the sensor makes 0 counts, and the sketch reads 0.0 lx. 1.0 lx gives 1 count = 0.8 lx, the largest error of the sweep, 16.7 %, and 3.0 lx gives 4 counts = 3.3 lx (+11.1 %). From 10 lx on, every reading is exact: 120 counts at 100 lx, 36000 at 30 klx. At 60 and 100 klx, the counts stop at 65535, and the sketch reads 54612.5 lx: the sensor is saturated. The MTreg table shows the trade-off: MTreg 31 reaches 121557 lx with 1.85 lx steps, while MTreg 254 has 0.23 lx steps but stops at 14836 lx. Good for: seeing the resolution and the range of your mode and MTreg, and where the sensor saturates.
Settings and Help
The gear opens Settings: theme (TEP Dark or Light), text size, open the panel at Run and the window size, saved for your Windows user. Look at the header: U2 · POWER DOWN. I opened Settings while U4 ran one-time readings, and the sensor sleeps between them.
The ? icon opens Help & Support: eight cards with their links (report a bug and suggest a feature on the BH1750 board of our forum, this article, the user guide, updates, donate, website, forum) and Copy diagnostics for a bug report. A links.ini next to the DLL can change the links.
BH1750 Night Lamp with Arduino in Proteus
Open BH1750-ArduinoUnoV3.pdsprj from the Proteus Simulation folder, with the .hex file and both DLLs beside it. Its two Arduino UNOs from our Arduino Library for Proteus V3.0 both run BH1750_Demo.hex:
- NODE 1 - SIMPLE, ADDR LOW = 0x23: UNO 1 (ARD1) with the BH1750 Simple (U1), the night lamp D1 on A0 and Serial Monitor U3.
- NODE 2 - ADVANCE, ADDR HIGH = 0x5C: UNO 2 (ARD2) with the BH1750 Advance (U2), the night lamp D2 on A0 and Serial Monitor U4.
Wiring
| From | To | Why |
|---|---|---|
| BH1750 VCC / GND | Supply / ground | Power |
| BH1750 SCL / SDA | Arduino A5 / A4 | I2C clock and data (the UNO's I2C pins) |
| U1 ADDR | Ground | Address 0x23 (open works too) |
| U2 ADDR | Supply | Address 0x5C |
| LED + 220 Ω | A0 to GND | The night lamp |
| Serial Monitor RXD / TXD / GND | Arduino D1 / D0 / ground | Shows what the Arduino prints, sends what you type |
In Proteus, the UNO's Wire library uses its internal pull-ups on A4 / A5, as in the v1.0 demo; the model only pulls SDA LOW. RXD2, the monitor's second input, is not used: I2C has no serial line to watch. See our Introduction to Arduino UNO for the pins.
The Arduino Code
The sketch needs Wire and the BH1750 library by Christopher Laws, included in the package:
#include <Wire.h>
#include <BH1750.h>
BH1750 lightMeter;
New in v1.1, the sketch finds the sensor at either address. A bare address write, a probe, that gets an ACK means a device answers there:
byte findSensor() {
const byte candidates[2] = {0x23, 0x5C};
for (byte i = 0; i < 2; i++) {
Wire.beginTransmission(candidates[i]);
if (Wire.endTransmission() == 0) return candidates[i];
}
return 0;
}
In setup(), Wire runs at 100 kHz with a 25 ms timeout, as in v1.0:
Wire.begin();
Wire.setClock(100000);
Wire.setWireTimeout(25000, true);
Then begin() starts continuous H-resolution at the address found:
sensorAddress = findSensor();
sensorReady = sensorAddress && lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE, sensorAddress);
begin() sends 10 and sets MTreg 69, which sends 42, 65 and the mode again: exactly the set-up the I2C check showed. The loop() reads every 500 ms with millis() instead of delay(), so typed commands stay responsive:
void loop() {
readCommands();
if (sensorReady && every && millis() - lastReading >= every) {
lastReading = millis();
takeReading();
}
}
takeReading() is the v1.0 reading plus a one-time branch: send the mode, wait for the measurement, then read:
void takeReading() {
if (oneTime) { // one-time: start a measurement, wait for it, then read (the chip powers down after it)
lightMeter.configure(modeFor(true, resolution));
delay(measureMs(true));
}
const float lux = lightMeter.readLightLevel();
The night lamp switches on below NIGHT lx (50 by default):
if (!lampOn && lux < nightLux) {
lampOn = true;
digitalWrite(A0, HIGH);
It switches off only above 1.2 times the threshold, so it cannot flicker around it (hysteresis), and it prints a line only when it switches:
} else if (lampOn && lux > nightLux * 1.2) {
lampOn = false;
digitalWrite(A0, LOW);
To change the sketch, export a new HEX file and load it into both UNOs; see How to get the HEX file from Arduino. New to Arduino? Start with our Arduino Tutorial for Beginners.
A Bug in the BH1750 Library: MTreg above 182
While building v1.1, we found a bug in the claws library 1.3.0. measurementReady(true) tells a one-time sketch when the maximum measurement time has passed, and it computes 180 x MTreg in an int. On the UNO, an int has 16 bits and holds at most 32767. Up to MTreg 182, 180 x 182 = 32760 fits; at MTreg 183, 180 x 183 = 32940 overflows to a negative number. Stored in the library's unsigned long, that becomes a wait of about 49 days, so measurementReady(true) never says ready, and a one-time sketch that waits for it hangs.
The demo avoids the bug: it does not call measurementReady(), but works out the wait itself in unsigned long (32 bits):
// the measurement time of the mode in use at this MTreg (datasheet maximum: 180 / 24 ms at MTreg 69)
unsigned long measureMs(bool maximum) {
const unsigned long base = resolution == 2 ? (maximum ? 24UL : 16UL) : (maximum ? 180UL : 120UL);
return base * mtreg / 69 + 1;
}
180UL makes the multiplication 32-bit: 180 x 254 / 69 + 1 = 663 ms at MTreg 254. The sketch's header notes it, too:
// Note: the library's measurementReady(true) computes 180 * MTreg in a 16-bit int on AVR and overflows for MTreg > 182, so
// the one-time wait below is computed here in unsigned long.
In your own one-time sketches, compute the wait the same way, or keep MTreg at 182 or below.
Serial Monitor Commands
| Command | What it does |
|---|---|
| HELP | The command list (two lines) |
| STATUS | Address, mode, MTreg, reading interval, readings, the last value and the night lamp (two lines) |
| MODE H / H2 / L | H-resolution (1 lx steps, 120 ms), H-resolution2 (0.5 lx), L-resolution (4 lx, 16 ms) |
| ONCE / CONT | A one-time measurement before every reading (the sensor powers down between) / continuous |
| MTREG n | 31 - 254 (69 = default): more sensitive, finer and slower |
| EVERY ms | The reading interval, 100 - 10000 ms (0 = pause) |
| NIGHT lx | The night lamp's threshold (default 50 lx, 0 = lamp off) |
| READ | One reading now |
Commands are not case-sensitive; an unknown one gets "Unknown command: X - type HELP". Every line the sketch prints is shorter than 80 characters, so it fits the Serial Monitor's Simple interface.
How to Run the Demo
- Press Run. Both Serial Monitors (9600 baud) and U2's pop-up open, and the I2C LEDs flash with every reading.
- On U1's panel, click NIGHT (D1 lights), then SUN; close and reopen the panel.
- On U3, click STATUS, MODE L, MODE H and NIGHT 200 (try ROOM and OFFICE); type MODE H2 at SUN, EVERY 0 and READ.
- On U4, click ONCE and MTREG 254, then STATUS.
- In U2's pop-up, try the Light page, then run the four tools on the Test tab.
BH1750 Proteus Simulation Results
The Serial Monitor in Its Simple Interface
Both monitors run in the Simple interface of our TEP Serial Monitor, as in every monitor screenshot here: only the log, the send box and one slim toolbar row (baud rate, follow, pause, search, clear) stay visible, so the window stays small next to your schematic.
- Switch to it: right-click the log and choose Simple interface, or press Ctrl+Shift+U.
- Back to the full view: click the corner button (the expand arrows at the top right), press Esc or Ctrl+Shift+U, or right-click and choose Full interface.
- Your choice is remembered.
Type commands such as STATUS or MODE H2 in the send box and press Enter; the quick buttons return with the full view. Lines you send carry a blue TX tag, the Arduino's lines a green RX tag. Read all about the monitor in TEP Serial Monitor Advance for Proteus.
Start-Up: The Sensor Is Found
After Run, U3 prints the banner, the address it found and the commands:
TEP BH1750 light sensor demo v1.1
BH1750 found at 0x23 (ADDR LOW): continuous H-resolution, MTreg 69
Serial Monitor: HELP, STATUS, MODE H / H2 / L, ONCE, CONT,
MTREG n, EVERY ms, NIGHT lx, READ
Light: 100.0 lx
Light: 100.0 lx
Then "Light: 100.0 lx" follows every 500 ms, because LUX starts at 100. U4 prints the same with "found at 0x5C (ADDR HIGH)".
The Night Lamp at Work
Next, I clicked OFFICE, NIGHT and SUN on U1's panel:
Light: 0.8 lx
Light: 500.0 lx
Night lamp OFF (500.0 lx)
Light: 500.0 lx
Light: 500.0 lx
Light: 0.8 lx
Night lamp ON (0.8 lx < 50 lx)
Light: 0.8 lx
Light: 0.8 lx
Light: 14104.2 lx
Night lamp OFF (14104.2 lx)
Light: 30000.0 lx
Light: 30000.0 lx
OFFICE switched the lamp off. NIGHT gave 0.8 lx and Night lamp ON (0.8 lx < 50 lx), and D1 lit. Then SUN: the next reading, 14104.2 lx, is an in-between measurement. The step fell in the middle of it, so it saw roughly half 0.8 lx and half 30000 lx. That is far above 60 lx (1.2 x 50), so the lamp went off at once. The step from 500 to 0.8 lx gave no in-between line: in 500 ms the sensor finishes about four measurements, and the sketch reads only one of them.
STATUS
Type STATUS (or click its quick button) at night, and U3 answers in two lines:
STATUS
Status: 0x23 (ADDR LOW), continuous H-resolution, MTreg 69, every 500 ms
last: 0.8 lx after 99 readings, night lamp on (below 50 lx)
MODE H2: Finer Steps, Half the Range
At SUN, I typed MODE H2:
Light: 30000.0 lx
MODE H2
Mode: continuous H-resolution2 (0.5 lx steps, 120 ms)
Light: 27306.2 lx
Light: 27306.2 lx
H-resolution2 counts twice as fine, so 30000 lx would need 72000 counts. The sensor stops at 65535, and the library prints 27306.2 lx: saturated. MODE H goes back. Right after a mode change, one reading can be off by a factor of 2 (at SUN: 15000.0 lx once): the library switches its formula at once, but the data register holds the old mode's result until the first new measurement ends. The real sensor does the same, so wait one measurement time after a mode change. In this run, the first read came late enough.
ONCE and MTREG 254 on U4
On U4, I clicked ONCE and MTREG 254, then typed STATUS:
One-time mode: a new H-resolution measurement before every reading
Light: 500.0 lx
Light: 500.0 lx
Light: 500.0 lx
MTREG 254
MTreg 254: 3.68 x sensitivity, 441 ms per measurement
Light: 500.1 lx
Light: 500.1 lx
STATUS
Light: 500.1 lx
Status: 0x5C (ADDR HIGH), one-time H-resolution, MTreg 254, every 500 ms
last: 500.1 lx after 241 readings, night lamp off (below 50 lx)
MTREG 254 makes the sensor 3.68 times as sensitive (254 / 69), and the library divides by 3.68 again, so the readings stay at 500 lx. The extra 0.1 lx is rounding: 500 x 1.2 x 254 / 69 = 2208.7 counts, rounded to 2209, gives 500.07 lx. After STATUS, one more Light line came first, because the sketch was waiting for a one-time measurement when the command arrived.
Your v1.0 Designs Keep Working
I opened the v1.0 release demo (the v1.0 symbol, the v1.0 sketch and the Virtual Terminal) with the v1.1 TEPBH1750.DLL:
It shows the v1.0 panel exactly as before and printed "Light: 100.0 lx"; a click on the slider set 1180.0 lx, and the terminal printed "Light: 1180.0 lx". Re-pick the part when you want the v1.1 board.
Troubleshooting
- "Sensor not found: check VCC, GND, SDA, SCL and ADDR.": check VCC / GND (the PWR LED must be lit), SCL on A5, SDA on A4 and the address against the ADDR pin (the ADDR box shows it); restart the simulation after the fix.
- "Light: 0.0 lx" right after the start: a read before the first measurement is done returns 0, on the real chip too.
- A reading between the old and the new light: correct; the BH1750 averages the light over its measurement.
- The reading stops at 54612.5 lx (27306.2 in H-resolution2): the sensor is saturated; use MTREG 31 for bright light.
- Your one-time sketch hangs with MTreg above 182: the measurementReady(true) bug above.
- The Test tab says "Cannot start: ...": no power, the sketch has not started a measurement yet, or another test runs.
- No panel, pop-up or monitor: a DLL is missing from MODELS and the project folder; click OPEN PANEL or OPEN MONITOR, or turn "Open the panel at Run" back on in Settings.
- The board looks like v1.0 (no red X): the design holds the v1.0 symbol; replace the LIB, delete TEPBH1750.IDX and re-pick the part.
- A lamp drawn near the sensor changes nothing: the slider, the scenes and the LUX properties are the light; the model has no optics.
Things to Know Before Using a Real BH1750 (GY-302)
The demo sketch uses only Wire and the claws library, so it runs on a real UNO and GY-302, too. Keep in mind:
- Supply: VCC 3.3 V or 5 V. The GY-302 has a 3.3 V regulator (662K), and most boards work from a 5 V UNO directly, but check yours: some sellers recommend a level shifter.
- Pull-ups: the GY-302 carries 4.7 k pull-ups on SCL and SDA. In Proteus, the demo relies on the UNO's internal pull-ups instead.
- Address: ADDR LOW or open = 0x23, ADDR HIGH (at least 0.7 x VCC) = 0x5C; two sensors on one bus need one of each.
- Accuracy: 1.2 counts per lux typical, 0.96 - 1.44, so a real sensor can read up to 20 % high or low; calibrate it against a known light meter.
- Range: about 0.8 - 54612 lx in H-resolution at MTreg 69. Direct sunlight (up to 100 klx) saturates it, so use MTreg 31 (121557 lx) or put a diffuser in front of it.
- Slow at high MTreg: MTreg 254 takes 442 ms typical, 663 ms maximum.
- Flicker: 50 / 60 Hz flicker averages out over a 120 ms H-resolution measurement; a 16 ms L-resolution measurement does not average it.
Limitations of the Simulation
- Not modelled: light flicker, spectral response and angle, temperature drift, the DVI reset pin (tied on the GY-302), the real chip's 400 kHz I2C limit and bus capacitance, and the 3.3 V regulator.
- The model is our own implementation, written from ROHM's published BH1750FVI data sheet; it contains no ROHM code.
- Tested in Proteus 8.5 on 8 Oct 2026 with this package's demo (both panels, the four tools, a v1.0 design and every screenshot here) and with 1,475 automatic PC checks, all passed, 58 of them with the unmodified claws library and 50 with the real demo sketch on two UNOs. Proteus 7 is not supported.
Need temperature, humidity and pressure next to the light? See our BME280 Sensor Library for Proteus. To send your readings to another Arduino by radio, add our HC-12 Library for Proteus.
So, that was all about the BH1750 Light Sensor Library for Proteus V1.1. I hope the sky over the board, the in-between readings and the four test tools make light measurement much easier to understand, so your night lamp or light logger works the first time you wire a real GY-302. If you use the BH1750 Light Sensor Library for Proteus in a project, please share your feedback in the comments or in our forum, and if you have any questions, ask in the comments and I will help you out. Till the next tutorial, take care and have fun!
























Comments
0