Skip to content

Starmbi/hp_BH1750

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

39 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Light-Sensor BH1750

a high-performance non-blocking library

the most comprehensive library avialable

tested with Arduino and ESP8266


Do you need...

  • the total control over this sensor?
  • exact timings?
  • fastest responses?
  • over to 100 or even 1000 samples per second?
  • an easy inteface with non-blocking readings?
  • inelligent autoranging functions?
  • extended range?

Try this library!


Take a look at the comprehensive Wiki!


Installation

Click "Clone or download" -> "Download ZIP" button.

  • (For Arduino >= 1.5.x) Use the way above, or Library Manager. Open Arduino IDE, click Sketch -> Include library -> Add .ZIP library and select the downloaded archive.

  • (For Arduino < 1.5.x) Extract the archive to <Your User Directory>/My Documents/Arduino/libraries/ folder and rename it to hp_BH1750. Restart IDE.


My intention to write this library:

For a project I need the exact timings for the start and the end time of a conversion.
This chip is well suited if you need a value every few seconds or minutes.
While the sensor provides good results, the design is terrible and makes it nearly impossible to obtain the time of beginning either of ending of a measurement.
To understand this problem you should know the working principle of this chip:

Working principle of BH1750

The sensor can measure in 3 different qualities: one low quality mode and two high quality modes.
To enhance the range, the sampling time is adjustable from 31 to 254.
However, this is not a measuring time in seconds or milliseconds, but it is called Measurement Time Register = MTreg.
At auto ranging you can read more about MTreg.

So let's take a look at the datasheet

Parameter Symbol Min. Typ. Max. Units Conditions
H-Resolution Mode Measurement Time tHR- 120 180 ms
L-Resolution Mode Measurement Time tlR- 16 24 ms

These timings refer to the default MTreg value of 69.
As you can see, for high quality we have a deviation between 120 and 180 milli seconds! Thats is a difference of 50%!

This difference of time is chip dependend and will never change.

If you bought a good chip, the time is close to 120 ms, if you bought a bad chip it is 180 ms.

To be sure to always read the newest value we have to wait at least 180 ms at MTreg 69.
Why should we wait 180 ms if we have a good chip that is finished after 120 ms?
But how do we find out how good our chip really is? Unfortunately it is not that easy.

  • The first bad news: The chip will never notify us when there is a new value.
  • The second bad news: the last measured value always remains in the data register.

So if you start a measurement and read out the value directly afterwards, you will not get a new value, but the value of the last measurement before that.
The only way to get the result as fast as possible, is to ask the sensor until you get a different result.
If you have a busy application this is no good solution, as communication over the I2C bus consumes a lot of time.
And if the brightnesses are the same for successive measurements, you will never notice when you get a new result.

And here comes one trick of the library:

It is possible to reset the data register to zero (0) before starting a measurement.
If we ask the sensor for a result, directly after starting a new measurement, we will get a result of zero.
So we ask the sensor for a new value, until we get a value greater than zero.
(Yes we need a little amount of light for this.)
After getting a new result, we can stop the time since the start of the measurement.

If we get a measurement time of 120 ms at MTreg=69, congratulations, we got a good chip.

The data sheet describes that the MTreg value is proportional to the measuring time.

So, with this true conversion time we can calculate the conversion time for every MTreg! But is this true?
Here I made a few measurements with the trick above:

At first glance, the formula seems to be right.
We see that for both qualities (High/Low) the conversion time is proportional to the MTreg.
But if we take a closer look, we notice that the two straight lines do not meet at the zero point, but 1.5 ms above it.
Independent of the sampling time, my chip needs additional 1.5 ms to provide the value. Of course, the execution code also contributes to this by some microseconds.

To take care of this offset we need 2 readings at different MTreg's for each quality (low & high). With this timings we can calculate the expected time for each combination of MTreg and quality!
Don't worry, the function calibrateTimings() will do the job for us.

After this function has been executed, we can measure at the fastest speed for each MTReg and quality!

And here is a working example of non-blocking readings at the fastest possible time:

#include <hp_BH1750.h>              //include the library
hp_BH1750 sensor;                   //define the sensor
void setup()
{
  sensor.begin(BH1750_TO_GROUND);            //A: set address and init sensor
  sensor.calibrateTiming();         //B: calibrate the timings, about 855ms with a bad chip
  sensor.start();            //C: start the first measurement
}
void loop()
{
  if (sensor.hasValue()) {          //D: most important function of this library!
    float lux = sensor.getLux();   //E: read the result
    sensor.start();          //F: start next measurement
  }
  // do a lot of other stuff here     G://
}

Some explanations to the code:

At line A: We set the address (address pin BH1750_TO_GROUND or BH1750_TO_VCC) Also we init the timing parameters according to the datasheet to the most pessimistic values. This ensures that the sensor will works correctly even with no calibration.

At line B: We calibrate the Sensor. For this we use by default the highest and lowest MTreg's (31 and 254) at both qualitys. You can change the two MTreg-values, if you want, for example sensor.calibrateTiming(50,150);
Since this only needs to be done once for each chip, the values can be stored in the eeprom or set directly in the code after test measurements. This library offers the appropriate functions for this.
The easiest way is to always calibrate the sensor in the setup section, as shown above.

At line C: We start the measurement with default quality and MTreg. You can change this values, for example sensor.start(BH1750_QUALITY_HIGH, 100);

At line D: Here comes the magic. At starting time the sensor sets a internal timer and calculates the conversion time for the given quality and MTreg.
If you ask sensor.hasValue() the function will return immediately without reading a value if the calculated conversion time is not finished yet. This is 30 times faster than asking the sensor!

So we jump directly to line G: and can read other sensors, do some calculations, wait for keypresses and so on...
Now we cycle into the loop until the calculated conversion time is over. Then the sensor will do a true read and delivers the result in line E:.

If you are satisfied with a blocking read, the code becomes even easier:

void loop()
{
  sensor.start();                 //start measurement
  float lux = sensor.getLux();   //read the result
  
}

As I described above, the sensor will only return with a result if it is greater than zero.
But what if it is very dark and the result is really zero?
In this case the sensor waits for an adjustable timeout and then outputs zero as the result. A timeout of 5-10 ms is sufficient.

Another notable feature of this library is the

Autoranging function

Why autoranging?

Here a table for comparison of the range and precision of different MTreg's and qualities:

Quality MTreg resolution lux highest lux time
LOW 31 7.4 121557 7
LOW 254 0.9 14836 59
HIGH 31 1.85 121557 54
HIGH 254 0.23 14836 442
HIGH2 31 0.93 60778 54
HIGH2 254 0.11 7418 442

Internally each lux value is calculated from a 16-bit value (two bytes). This covers a range from 0-65535 digits.
You can read it with sensor.getRaw() instead or additional to sensor.getLux().

At each MTreg the lowest value is always 0 Lux .
Only the maximal lux value differs at different MTreg's.

With the highest MTReg of 254 you can reach only 7418 Lux, but you have a high resolution of 7418/65535 = 0.11 Lux.
With the lowest MTreg of 31 you can reach 60778 Lux, but only have a resolution of 60778/65535 = 0.93 Lux.

If you need more then 60778 Lux you have to switch to from quality HIGH2" to quality HIGH.
This doubles your range to 121557 Lux, but also halves the resolution.

Now let's explain the difference between quality LOW and HIGH
As you can see: quality LOW and quality HIGH have the same range (highest lux).
But the conversion time of LOW is 7.5 times faster than HIGH (442 ms against 59ms at MTreg 254).
The drawback is the resolution: LOW x is 4 times less sensitive:

With quality HIGH you will read raw data like: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ...
With quality LOW you will read raw data like: 0, 0, 0, 0, 4, 4, 4, 4, 8, 8, ...

But 4 times less sensitivity against 7.5 times faster conversion time is a good deal!

To obtain the highest resolution you should change the MTreg and quality according to your brightness.

After each measurement the MTReg should be adjusted, so that the measured brightness is at the upper limit of the MTreg range.
But this can be dangerous:
If the brightness increases, you are out of the range of your maximal value an the sensor is saturated.
So it is better to go a few percent lower than the maximal value of the MTreg.
To minimize the change, being overexposed, you can set the MTreg so that your value is in the middle of the range e.g to 50 %.

Does it all sound pretty complicated?
With one line of code all this can be done for you!

{
 if (sensor.hasValue()) {          
   float lux = sensor.getLux();   
   sensor.adjustSettings(90);     //new line!
   sensor.start();          
 }

Please note the new line sensor.adjustSettings(90); just before starting a new measurement.
This is a very powerfull function. It takes the last measurement and calculates the new MTReg and quality so that we are in the desired range. This range is passed to the function as parameter, in this example, 90 %.

If the function detects, that the last result was overexposed, it takes a short measurement (~10 ms) with the lowest quality and resolution and calculate from this value the new Mtreg and quality. This short measurement is blocking!

If there are longer periods between the measurements, the last value is not so meaningful for a new calculation, because the brightness may have changed. In this case it would be better to alway's take a short measurement before the high resolution measurement. You will loose about 10 ms but at low intensities you do not have to repeat a 500 ms measurement.
You can easily force this with a second parameter for the function:
sensor.adjustSettings(90,TRUE); Here the declaration of the function:
void adjustSettings(byte Percent = 50, bool forcePreShot=false);

If you start the measurement in quality HIGH OR HIGH2 this function will switch between this qualities if necessary.
If you start the measurement in quality LOW it will stay at this quality because it asumes that you want the highest speed.