├── .gitignore
├── LICENSE
├── README.md
├── examples
├── BareMinimum
│ └── BareMinimum.ino
├── Non_blocking
│ └── Non_blocking.ino
├── SampleRate
│ └── SampleRate.ino
├── Saturated
│ └── Saturated.ino
├── Sweep_MTreg
│ └── Sweep_MTreg.ino
├── TestSuite
│ └── TestSuite.ino
└── TwoSensors
│ └── TwoSensors.ino
├── keywords.txt
├── library.properties
└── src
├── hp_BH1750.cpp
└── hp_BH1750.h
/.gitignore:
--------------------------------------------------------------------------------
1 | # Arduino
2 | .development
3 |
4 | # Visual Studio code
5 | .vscode/
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Stefan Armborst
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Light-Sensor BH1750 [](https://github.com/Starmbi/hp_BH1750/wiki)
2 | ## a high-performance non-blocking library
3 | ### the most comprehensive library avialable
4 | #### tested with Arduino and ESP8266
5 | -------------
6 |
7 |
8 |
9 | ### Do you need...
10 | * the total control over this sensor?
11 | * exact timings?
12 | * fastest responses?
13 | * over to 100 or even 1000 samples per second?
14 | * an easy inteface with non-blocking readings?
15 | * inelligent autoranging functions?
16 | * extended range?
17 |
18 | ### Try this library!
19 | -----
20 |
21 | ### Take a look at the comprehensive *[Wiki](../../wiki)!*
22 |
23 | -------------
24 | ## Installation
25 |
26 | Click "Clone or download" -> "Download ZIP" button.
27 |
28 | - **(For Arduino >= 1.5.x)** Use the way above, or Library Manager. Open Arduino
29 | IDE, click `Sketch -> Include library -> Add .ZIP library ` and select the
30 | downloaded archive.
31 |
32 | - **(For Arduino < 1.5.x)** Extract the archive to
33 | ``/My Documents/Arduino/libraries/`` folder and rename it
34 | to `hp_BH1750`. Restart IDE.
35 | ---------------
36 |
37 | ## My intention to write this library:
38 |
39 | For a project I need the exact timings for the start and the end time of a conversion.
40 | This chip is well suited if you need a value every few seconds or minutes.
41 | 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.
42 | To understand this problem you should know the working principle of this chip:
43 |
44 | ## Working principle of BH1750
45 |
46 | The sensor can measure in 3 different qualities: one ```low quality``` mode and two ```high quality``` modes.
47 | To enhance the range, the sampling time is adjustable from 31 to 254.
48 | However, this is not a measuring time in seconds or milliseconds, but it is called **M**easurement **T**ime **Reg**ister = ***MTreg***.
49 | At [auto ranging](#autoranging-function) you can read more about *MTreg*.
50 |
51 | So let's take a look at the [datasheet](https://www.mouser.com/datasheet/2/348/bh1750fvi-e-186247.pdf "BH1750")
52 |
53 | | Parameter | Symbol | Min. | Typ. | Max. | Units | Conditions |
54 | |:----------|:-------|-----:|-----:|-----:|-------|------------|
55 | | H-Resolution Mode Measurement Time | **t**HR- | | 120 | 180 | ms | |
56 | | L-Resolution Mode Measurement Time | **t**lR- | | 16 | 24 | ms | |
57 |
58 | These timings refer to the default *MTreg* value of 69.
59 | As you can see, for ```high quality``` we have a deviation between 120 and 180 milli seconds! Thats is a difference of 50%!
60 |
61 | ### This difference of time is chip dependend and will never change.
62 |
63 | If you bought a good chip, the time is close to 120 ms, if you bought a bad chip it is 180 ms.
64 |
65 | To be sure to always read the newest value we have to wait at least 180 ms at *MTreg* 69.
66 | Why should we wait 180 ms if we have a good chip that is finished after 120 ms?
67 | But how do we find out how good our chip really is? Unfortunately it is not that easy.
68 |
69 | * The first bad news: The chip will never notify us when there is a new value.
70 | * The second bad news: the last measured value **always remains in the data register**.
71 |
72 | 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.
73 | The only way to get the result as fast as possible, is to ask the sensor until you get a different result.
74 | If you have a busy application this is no good solution, as communication over the I2C bus consumes a lot of time.
75 | And if the brightnesses are the same for successive measurements, you will never notice when you get a new result.
76 |
77 | ### And here comes one trick of the library: ###
78 | It is possible to reset the data register to zero (0) before starting a measurement.
79 | If we ask the sensor for a result, directly after starting a new measurement, we will get a result of zero.
80 | So we ask the sensor for a new value, until we get a value greater than zero.
81 | (Yes we need a little amount of light for this.)
82 | After getting a new result, we can stop the time since the start of the measurement.
83 |
84 | If we get a measurement time of 120 ms at *MTreg*=69, congratulations, we got a good chip.
85 |
86 | The data sheet describes that the ***MTreg* value is proportional to the measuring time**.
87 |
88 | So, with this true conversion time we can calculate the conversion time for every *MTreg*! But is this true?
89 | Here I made a few measurements with the trick above:
90 |
91 | 
92 |
93 | At first glance, the formula seems to be right.
94 | We see that for both qualities (High/Low) the conversion time is proportional to the *MTreg*.
95 | 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.
96 | 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.
97 |
98 | To take care of this offset we need 2 readings at different *MTreg's* for each quality (low & high).
99 | With this timings we can calculate the expected time for each combination of *MTreg* and quality!
100 | Don't worry, the function ```calibrateTimings()``` will do the job for us.
101 |
102 | After this function has been executed, we can measure at the fastest speed for each *MTReg* and quality!
103 |
104 | And here is a working example of non-blocking readings at the fastest possible time:
105 | ```C++
106 | #include //include the library
107 | hp_BH1750 sensor; //define the sensor
108 | void setup()
109 | {
110 | sensor.begin(BH1750_TO_GROUND); //A: set address and init sensor
111 | sensor.calibrateTiming(); //B: calibrate the timings, about 855ms with a bad chip
112 | sensor.start(); //C: start the first measurement
113 | }
114 | void loop()
115 | {
116 | if (sensor.hasValue()) { //D: most important function of this library!
117 | float lux = sensor.getLux(); //E: read the result
118 | sensor.start(); //F: start next measurement
119 | }
120 | // do a lot of other stuff here G://
121 | }
122 | ```
123 | ### Some explanations to the code:
124 | ```At line A:``` We set the address (address pin BH1750_TO_GROUND or BH1750_TO_VCC)
125 | Also we init the timing parameters according to the datasheet to the most pessimistic values.
126 | This ensures that the sensor will works correctly even with no calibration.
127 |
128 | ```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.
129 | You can change the two MTreg-values, if you want, for example ```sensor.calibrateTiming(50,150);```
130 | 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.
131 | The easiest way is to always calibrate the sensor in the *setup* section, as shown above.
132 |
133 | ```At line C:``` We start the measurement with default quality and *MTreg*.
134 | You can change this values, for example ```sensor.start(BH1750_QUALITY_HIGH, 100);```
135 |
136 | ```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*.
137 | 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!
138 |
139 | So we jump directly to ```line G:``` and can read other sensors, do some calculations, wait for keypresses and so on...
140 | 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:.
141 |
142 | If you are satisfied with a blocking read, the code becomes even easier:
143 |
144 | ```C++
145 | void loop()
146 | {
147 | sensor.start(); //start measurement
148 | float lux = sensor.getLux(); //read the result
149 |
150 | }
151 | ```
152 |
153 | As I described above, the sensor will only return with a result if it is greater than zero.
154 | But what if it is very dark and the result is really zero?
155 | 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.
156 |
157 | Another notable feature of this library is the
158 | ## Autoranging function
159 | Why autoranging?
160 |
161 | Here a table for comparison of the range and precision of different MTreg's and qualities:
162 |
163 | | Quality | MTreg | resolution lux | highest lux | time |
164 | |---------|------:|-------------:|-----------:|-------:|
165 | | LOW | 31 | 7.4 | 121557 | 7 |
166 | | LOW | 254 | 0.9 | 14836 | 59 |
167 | | HIGH | 31 | 1.85 | 121557 | 54 |
168 | | HIGH | 254 | 0.23 | 14836 | 442 |
169 | | HIGH2 | 31 | 0.93 | 60778 | 54 |
170 | | HIGH2 | 254 | 0.11 | 7418 | 442|
171 |
172 | Internally each lux value is calculated from a 16-bit value (two bytes). This covers a range from 0-65535 digits.
173 | You can read it with ```sensor.getRaw()``` instead or additional to ```sensor.getLux()```.
174 |
175 | At each *MTreg* the lowest value is always 0 Lux .
176 | Only the maximal lux value differs at different *MTreg's*.
177 |
178 | With the highest *MTReg* of 254 you can reach only **7418** Lux, but you have a high resolution of 7418/65535 = **0.11** Lux.
179 | With the lowest *MTreg* of 31 you can reach **60778** Lux, but only have a resolution of 60778/65535 = **0.93** Lux.
180 |
181 | If you need more then **60778** Lux you have to switch to from quality ```HIGH2```" to quality ```HIGH```.
182 | This doubles your range to **121557** Lux, but also halves the resolution.
183 |
184 | Now let's explain the difference between quality ```LOW``` and ```HIGH```
185 | As you can see: quality ```LOW``` and quality ```HIGH``` have the same range (highest lux).
186 | But the conversion time of ```LOW``` is 7.5 times faster than ```HIGH``` (442 ms against 59ms at MTreg 254).
187 | The drawback is the resolution: ```LOW``` x is 4 times less sensitive:
188 |
189 | With quality ```HIGH``` you will read raw data like: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ...
190 | With quality ```LOW ``` you will read raw data like: 0, 0, 0, 0, 4, 4, 4, 4, 8, 8, ...
191 |
192 | But 4 times less sensitivity against 7.5 times faster conversion time is a good deal!
193 |
194 | ***To obtain the highest resolution you should change the MTreg and quality according to your brightness.***
195 |
196 | After each measurement the *MTReg* should be adjusted, so that the measured brightness is at the upper limit of the *MTreg* range.
197 | But this can be dangerous:
198 | If the brightness increases, you are out of the range of your maximal value an the sensor is saturated.
199 | So it is better to go a few percent lower than the maximal value of the *MTreg*.
200 | 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 %.
201 |
202 | Does it all sound pretty complicated?
203 | With one line of code all this can be done for you!
204 |
205 | ```C++
206 | {
207 | if (sensor.hasValue()) {
208 | float lux = sensor.getLux();
209 | sensor.adjustSettings(90); //new line!
210 | sensor.start();
211 | }
212 | ```
213 |
214 | Please note the new line ```sensor.adjustSettings(90);``` just before starting a new measurement.
215 | This is a very powerfull function.
216 | It takes the last measurement and calculates the new *MTReg* and quality so that we are in the desired range.
217 | This range is passed to the function as parameter, in this example, ```90``` %.
218 |
219 | 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!
220 |
221 | 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.
222 | You can easily force this with a second parameter for the function:
223 | ```sensor.adjustSettings(90,TRUE);``` Here the declaration of the function:
224 | ```void adjustSettings(byte Percent = 50, bool forcePreShot=false);```
225 |
226 | If you start the measurement in quality ```HIGH``` OR ```HIGH2``` this function will switch between this qualities if necessary.
227 | If you start the measurement in quality ```LOW``` it will stay at this quality because it asumes that you want the highest speed.
228 |
--------------------------------------------------------------------------------
/examples/BareMinimum/BareMinimum.ino:
--------------------------------------------------------------------------------
1 | //for help look at: https://github.com/Starmbi/hp_BH1750/wiki
2 | #include
3 | #include // include the library
4 | hp_BH1750 BH1750; // create the sensor
5 |
6 | void setup()
7 | {
8 | // put your setup code here, to run once:
9 | Serial.begin(9600);
10 | bool avail = BH1750.begin(BH1750_TO_GROUND);// init the sensor with address pin connetcted to ground
11 | // result (bool) wil be be "false" if no sensor found
12 | if (!avail) {
13 | Serial.println("No BH1750 sensor found!");
14 | while (true) {};
15 | }
16 | }
17 |
18 | void loop()
19 | {
20 | // put your main code here, to run repeatedly:
21 | BH1750.start(); //starts a measurement
22 | float lux=BH1750.getLux(); // waits until a conversion finished
23 | Serial.println(lux);
24 | }
25 |
--------------------------------------------------------------------------------
/examples/Non_blocking/Non_blocking.ino:
--------------------------------------------------------------------------------
1 | //for help look at: https://github.com/Starmbi/hp_BH1750/wiki
2 | #include
3 | #include //inlude the library
4 | hp_BH1750 BH1750; //create the sensor object
5 |
6 | void setup()
7 | {
8 | // put your setup code here, to run once:
9 | Serial.begin(9600);
10 |
11 | bool avail = BH1750.begin(BH1750_TO_GROUND); // will be false no sensor found
12 | // use BH1750_TO_GROUND or BH1750_TO_VCC depending how you wired the address pin of the sensor.
13 | if (!avail) {
14 | Serial.println("No BH1750 sensor found!");
15 | while (true) {};
16 | }
17 |
18 | // BH1750.calibrateTiming(); //uncomment this line if you want to speed up your sensor
19 | Serial.printf("conversion time: %dms\n", BH1750.getMtregTime());
20 |
21 | BH1750.start(); //start the first measurement in setup
22 | }
23 |
24 | void loop()
25 | {
26 | // put your main code here, to run repeatedly:
27 |
28 | if (BH1750.hasValue() == true) { // non blocking reading
29 | float lux = BH1750.getLux();
30 | Serial.println(lux);
31 | BH1750.start();
32 | }
33 | // do a lot of other stuff here, while sensor is waiting for the result...
34 | }
35 |
--------------------------------------------------------------------------------
/examples/SampleRate/SampleRate.ino:
--------------------------------------------------------------------------------
1 | // for help look at: https://github.com/Starmbi/hp_BH1750/wiki
2 | // to speed up the measurements you can change the BH1750_MTREG_LOW = 31
3 | // in the hp_BH1750.h file to a lower value
4 |
5 | #include
6 | #include // include the library
7 |
8 | //stores the first MAX_VAL samples in a array.
9 | const unsigned int MAX_VAL = 150; // for Arduino (low memory)
10 | // const unsigned int MAX_VAL = 1500; //for ESP8266
11 |
12 | unsigned int val[MAX_VAL]; // adjust the value to your free memory on the board.
13 | hp_BH1750 sens;
14 | void setup()
15 | {
16 | // put your setup code here, to run once:
17 | Wire.setClock(400000); // uncomment this line if you have problems with communication
18 | Serial.begin(9600);
19 | //Serial.begin(115200); // try this line for faster printing, uncomment the line above
20 | sens.begin(BH1750_TO_GROUND); // change to (BH1750_TO_VCC) if address pin connected to VCC
21 | sens.calibrateTiming(); // you need a little brightness for this
22 | }
23 |
24 | void loop()
25 | {
26 | // put your main code here, to run repeatedly:
27 | Serial.println("***********");
28 | unsigned int t = millis() + 1000;
29 | unsigned int c = 0;
30 | while (millis() <= t)
31 | {
32 | sens.start(BH1750_QUALITY_LOW, 1); //will be adjusted to lowest allowed MTreg (default = 31)
33 | if (c < MAX_VAL)
34 | {
35 | val[c] = sens.getRaw();
36 | }
37 | else
38 | {
39 | sens.getRaw();
40 | }
41 | yield(); // feed the watchdog of ESP6682
42 | c++;
43 | }
44 | unsigned int readVal = c;
45 | if (readVal > MAX_VAL)
46 | c = MAX_VAL;
47 | // Serial.print(c);
48 | // Serial.print(char(9));
49 | // Serial.println(sens.getRaw());
50 | for (unsigned int i = 0; i < c; i++)
51 | {
52 | //Serial.print(i);
53 | Serial.print(val[i]);
54 | Serial.print(char(9));
55 | if ((i + 1) % 10 == 0)
56 | Serial.println("");
57 | yield(); // feed the watchdog of ESP6682
58 | }
59 | Serial.println("");
60 | Serial.print(readVal);
61 | Serial.println(" Samples per second");
62 | Serial.println("");
63 | delay(1000);
64 | }
65 |
--------------------------------------------------------------------------------
/examples/Saturated/Saturated.ino:
--------------------------------------------------------------------------------
1 | //for help look at: https://github.com/Starmbi/hp_BH1750/wiki
2 | //
3 | // Please apply a lot of light to the sensor (more than 7417 lux).
4 | // that is the maximum brightness, the sensor is capable at high sensitivity.
5 | // Please note, that the first measurement with low quality shows higher values.
6 | // The second measurement will not get higher , because it's already saturated.
7 | // With a hack you can calulate the brightness from the saturated result.
8 | // For this, you use the reduced conversion time of the sensor, that happens if the sensor is saturated.
9 | // Divide the estimated time thru the measured time: "BH1750.getMtregTime() / BH1750.getTime()"
10 | // Multipy this factor to the saturated Value.
11 | // Compare this result to the first measurement with low quality.
12 | // With this hack you will get even valid results on high quality, when the measurement with low quality is already saturated!
13 | // For best results, close the serial monitor and open the "serial plotter" with .
14 |
15 | #include
16 | #include
17 | hp_BH1750 BH1750;
18 | void setup() {
19 | // put your setup code here, to run once:
20 | Serial.begin(9600);
21 | BH1750.begin(BH1750_TO_GROUND);// change to BH1750_TO_VCC if address pin is connected to VCC.
22 | BH1750.calibrateTiming();
23 | }
24 |
25 | void loop() {
26 | // put your main code here, to run repeatedly:
27 | BH1750.start(BH1750_QUALITY_LOW, 31); // starts a measurement at low quality
28 | Serial.print(BH1750.getLux()); // do a blocking read and waits until a result receives
29 | Serial.print(char(9));
30 | BH1750.start(BH1750_QUALITY_HIGH2, 254); // starts a measurement with high quality but restricted range in brightness
31 | float val = BH1750.getLux();
32 | Serial.print(val);
33 | Serial.print(char(9));
34 | if (BH1750.saturated() == true){
35 | val = val * BH1750.getMtregTime() / BH1750.getTime(); // here we calculate from a saturated sensor the brightness!
36 | }
37 | Serial.print(val);
38 | //Serial.print(char(9));
39 | //Serial.print(sens.getTime());
40 | Serial.println("");
41 | }
42 |
--------------------------------------------------------------------------------
/examples/Sweep_MTreg/Sweep_MTreg.ino:
--------------------------------------------------------------------------------
1 | // for help look at: https://github.com/Starmbi/hp_BH1750/wiki
2 | //
3 | // This demo tests if your sensor is suitable for lower MTreg-values than the manufacturer recommends.
4 | // In a loop, the default Mtreg of 69 is lowered to the lowest allowed MTreg of 31.
5 | // You will see a decreasing conversion time.
6 | //
7 | // If you want lower MTregs than 31, you have to hack this library!
8 | //
9 | // Please go to this library in your local library folder.
10 | // Often the path is: "C:\Users\\Documents\Arduino\libraries\hp_BH1750\src\".
11 | // Create a backup copy of the File "hp_BH1750.h"
12 | // Open the file "hp_BH1750.h" with a text editor and change the line:
13 | // "BH1750_MTREG_LOW = 31," to
14 | // "BH1750_MTREG_LOW = 1,".
15 | // Do not type the quotation marks!
16 | // Save the file and compile this sketch again.
17 | // Now you can check up to which MTReg your sensor works correctly.
18 | // It is quite unlikely that the sensor will be damaged, if operates outside its specification.
19 | // My sensors are working without problems with MTreg's down to 1! :-)
20 | //
21 | // To restore the original settings, change back to "BH1750_MTREG_LOW = 31,",
22 | // or restore the "hp_BH1750.h" with your backup copy.
23 | // You have to compile this sketch again.
24 |
25 | #include
26 | #include // include library
27 |
28 | hp_BH1750 BH1750; // create sensor object
29 | void setup() {
30 | // put your setup code here, to run once:
31 | Serial.begin(9600);
32 | BH1750.begin(BH1750_TO_GROUND);
33 | BH1750.calibrateTiming();
34 | }
35 |
36 | void loop() {
37 | // put your main code here, to run repeatedly:
38 | Serial.println("***********");
39 | for (int i = 69; i >=(int)BH1750_MTREG_LOW; i--) {
40 | BH1750.start(BH1750_QUALITY_LOW, i);
41 | float lux = BH1750.getLux();
42 | Serial.print(BH1750.getMtreg());
43 | Serial.print(char(9));
44 | Serial.print(lux);
45 | Serial.print(" Lux ");
46 | Serial.print(char(9));
47 | Serial.print(BH1750.getTime());
48 | Serial.println(" ms.");
49 | delay(100);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/examples/TestSuite/TestSuite.ino:
--------------------------------------------------------------------------------
1 | // for help look at: https://github.com/Starmbi/hp_BH1750/wiki
2 | // to speed up the measurements, please use a higher baud rate than the default 9600 baud.
3 | #include
4 | #include
5 | hp_BH1750 BH1750;
6 |
7 | unsigned int value;
8 | unsigned long loops = 0;
9 | byte percent = 50;
10 | byte mtreg = BH1750_MTREG_DEFAULT;
11 | bool show = true;
12 | bool fsens = false;
13 | bool preShot = false;
14 | bool autoQ = false;
15 | char buf[150];
16 | BH1750Timing ti;
17 | BH1750Address addr = BH1750_TO_GROUND;
18 | void printHelp()
19 | {
20 | Serial.println(F("*********************************************************************************************"));
21 | Serial.println(F("Enter all commands in the upper field of the serial monitor and press to submit."));
22 | Serial.println(F(""));
23 | Serial.println(F("Enter \"h\" to see this help again! "));
24 | Serial.println(F(""));
25 | Serial.println(F("Enter \"m\" followed by a number between 31 to 254, to change the quality"));
26 | Serial.println(F("Enter \"c\" to calibrate the sensor timing"));
27 | Serial.println(F("Enter \"i\" to init the sensor to factory timing"));
28 | Serial.println(F("Enter \"q\" followed by a number between 1 to 3, to change the quality"));
29 | Serial.println(F("Enter \"a\" to toggle between fixed MTreg and AUTORANGE"));
30 | Serial.println(F("Enter \"s\" to toggle between all informations or only for Lux. Try serial plotter!"));
31 | Serial.println(F("Enter \"o\" with a positive or negative number to change the time offset"));
32 | Serial.println(F("Enter \"t\" followed by a number to change the timeout in ms."));
33 | Serial.println(F("Enter \"f\" to toggle between forced readings or only waiting for the estimated conversion time"));
34 | Serial.println(F("Enter \"r\" to toggle between forced preShots in autorange mode"));
35 | Serial.print(F("*********************************************************************************************"));
36 | waitKeyPress();
37 | }
38 |
39 | void busyDelay(unsigned int ms)
40 | {
41 | unsigned long t = millis() + ms;
42 | while (millis() < t)
43 | BH1750.hasValue();
44 | }
45 |
46 | void flushQueue(unsigned int timeout)
47 | {
48 | while (Serial.available())
49 | Serial.read();
50 | busyDelay(timeout);
51 | while (Serial.available())
52 | {
53 | Serial.read();
54 | busyDelay(timeout);
55 | }
56 | }
57 |
58 | void waitKeyPress()
59 | {
60 | Serial.println(F(""));
61 | Serial.println(F("Press any key and to continue."));
62 | flushQueue(0);
63 | while (Serial.available() == 0)
64 | { //wait until keypressed
65 | }
66 | flushQueue(3);
67 | }
68 |
69 | String getQual()
70 | {
71 | BH1750Quality q = BH1750.getQuality();
72 | switch (q)
73 | {
74 | case BH1750_QUALITY_LOW:
75 | return "Low ";
76 | break;
77 | case BH1750_QUALITY_HIGH:
78 | return "High ";
79 | break;
80 | case BH1750_QUALITY_HIGH2:
81 | default:
82 | return "High2";
83 | break;
84 | }
85 | }
86 |
87 | void printCalib()
88 | {
89 | ti = BH1750.getTiming();
90 | sprintf(buf, "MTregs:\t\t %3i %3i", ti.mtregLow, ti.mtregHigh);
91 | Serial.println(buf);
92 | sprintf(buf, "Low quality:\t %3i %3i ms.", ti.mtregLow_qualityLow, ti.mtregLow_qualityHigh);
93 | Serial.println(buf);
94 | sprintf(buf, "High quality:\t %3i %3i ms.", ti.mtregHigh_qualityLow, ti.mtregHigh_qualityHigh);
95 | Serial.println(buf);
96 | Serial.println("");
97 | }
98 |
99 | void setup()
100 | {
101 | Serial.begin(9600);
102 | Serial.println("");
103 | if (BH1750.begin(addr) == false)
104 | {
105 | Serial.println("Sensor not found");
106 | }
107 |
108 | while (Serial.available() == 0)
109 | {
110 | Serial.println("enter any key and press to continue");
111 | delay(1000);
112 | }
113 | printHelp();
114 |
115 | //BH1750.calibrateTiming(); //you can calibrate the sensor-timings, every time with pressing
116 | BH1750.start(BH1750_QUALITY_HIGH2, mtreg);
117 | }
118 |
119 | void loop()
120 | {
121 | lookSerial();
122 | loops++;
123 | if (BH1750.hasValue(fsens))
124 | {
125 | if (show == true)
126 | {
127 | char q[6];
128 | getQual().toCharArray(q, 6);
129 | unsigned long t = (float)loops / (float)BH1750.getTime() * 1000;
130 | sprintf(buf, "Loops/sec %8lu | Reads %4u | T calc/is %4u %4u | Timeout %3i | Offset %4i | Quality %s | MTreg %3u | Raw %5u | Lux ",
131 | t, BH1750.getReads(), BH1750.getMtregTime(), BH1750.getTime(), BH1750.getTimeout(), BH1750.getTimeOffset(), q, BH1750.getMtreg(), BH1750.getRaw());
132 | Serial.print(buf);
133 | dtostrf(BH1750.getLux(), 9, 2, buf);
134 | Serial.print(buf);
135 | if (BH1750.saturated() == true) {
136 | Serial.print(" *");
137 | }
138 | else
139 | {
140 | Serial.print(" ");
141 | }
142 | Serial.print("| ");
143 | if (fsens == true)
144 | {
145 | Serial.print("F");
146 | }
147 | else
148 | Serial.print("-");
149 | if (autoQ == true)
150 | {
151 | Serial.print(" A ");
152 | Serial.print(BH1750.getPercent());
153 | Serial.print("%");
154 | if (preShot == true) Serial.print(" pS");
155 | }
156 | }
157 | else
158 | {
159 | Serial.print(BH1750.getLux());
160 | }
161 | Serial.println("");
162 | if (autoQ)
163 | BH1750.adjustSettings(percent, preShot);
164 | if (!BH1750.start())
165 | Serial.println("No sensor found");
166 | loops = 0;
167 | }
168 | }
169 |
170 | void lookSerial()
171 | {
172 | BH1750Quality mode;
173 | int v;
174 | BH1750Timing t;
175 | Serial.setTimeout(3);
176 | if (Serial.available() > 0)
177 | {
178 | busyDelay(3);
179 | //Serial.println("----------");
180 | v = Serial.read();
181 | if (v > 96)
182 | { //lowercase a
183 | byte lc;
184 | switch (v)
185 | {
186 | case 104: //h = help
187 | printHelp();
188 | break;
189 | case 109: //m setMtreg
190 | v = Serial.parseInt();
191 | if (v < 255 && v > 0)
192 | BH1750.writeMtreg(v);
193 | break;
194 | case 105: //i init
195 | BH1750.begin(addr);
196 | break;
197 | case 112: // p set percentage for autorange
198 | percent = Serial.parseInt();
199 | break;
200 | case 113: //q = sets BH1750Quality
201 | v = Serial.parseInt();
202 | if (v == 1)
203 | mode = BH1750_QUALITY_LOW;
204 | if (v == 2)
205 | mode = BH1750_QUALITY_HIGH;
206 | if (v == 3)
207 | mode = BH1750_QUALITY_HIGH2;
208 | BH1750.setQuality(mode);
209 | break;
210 | case 97: //a toggle autoranging on and off
211 | autoQ = !autoQ;
212 | break;
213 | case 99: //c calibrates the timing of the sensor
214 | ti = BH1750.getTiming();
215 | Serial.println(F("before Calibration"));
216 | printCalib();
217 | lc = BH1750.calibrateTiming(); // lc<2 = Calibration accepted
218 | switch (lc)
219 | {
220 | case BH1750_CAL_OK:
221 | Serial.println("Calibration succsessfull"); //0
222 | break;
223 | case BH1750_CAL_MTREG_CHANGED:
224 | Serial.print("Calibration partial succsessfull, MTREG adjustet to "); //1
225 | t = BH1750.getTiming();
226 | Serial.println(t.mtregHigh);
227 | break;
228 | case BH1750_CAL_TOO_BRIGHT:
229 | Serial.println("Calibration failed, too bright"); //2
230 | break;
231 | case BH1750_CAL_TOO_DARK:
232 | Serial.println("Calibration failed, too dark"); //3
233 | break;
234 | case BH1750_CAL_COMMUNICATION_ERROR:
235 | Serial.println("Communication Error"); //4
236 | break;
237 | }
238 | Serial.println(F("after Calibration"));
239 | printCalib();
240 | waitKeyPress();
241 | BH1750.start();
242 | break;
243 | case 111: // o change the time offset in ms.
244 | v = Serial.parseInt();
245 | BH1750.setTimeOffset(v);
246 | break;
247 | case 116: //t = timeout
248 | v = Serial.parseInt();
249 | BH1750.setTimeout(v);
250 | break;
251 | case 102: //f force true readings and endabled preShot in auto ranging mode
252 | v = Serial.parseInt();
253 | fsens = !fsens;
254 | break;
255 | case 114: //s
256 | preShot = !preShot;
257 | break;
258 | case 115: //s
259 | show = !show;
260 | break;
261 | }
262 | }
263 | flushQueue(3);
264 | }
265 | }
266 |
--------------------------------------------------------------------------------
/examples/TwoSensors/TwoSensors.ino:
--------------------------------------------------------------------------------
1 | //for help look at: https://github.com/Starmbi/hp_BH1750/wiki
2 | //
3 | // This is an example of non blocking reads with two sensors.
4 | //
5 | // If you work with 2 (or more) sensors, you have to consider,
6 | // that each sensor can have a different sampling time, even at the same settings.
7 | // To keep the sensors synchronized, you have to wait for the slowest sensor,
8 | // before you start the next measurements.
9 | // If you look into the loop, you will find a somewhat complicated query,
10 | // if the sensors have finished their measurement.
11 | // You can achieve the same behaviour, if you uncomment the line with "//**" and
12 | // comment the lines with "//*", if you only need the brightness of the two sensors.
13 | // But if you also need the conversion time of the two sensors, this shorter way will fail!
14 | //
15 | // look at the line:
16 | // if ((sens1.hasValue(forceReading) == true) && (sens2.hasValue(forceReading) == true)) {
17 | //
18 | // If sens1 has no value, sen2 will never be asked because the query is interrupted at this point!
19 | // At C++, if the first condition of an AND operation is already false, the second condition is never executed.
20 | // To get the timings of the sensorst, this will only work, if sens2 is the slower sensor.
21 | // To avoid that we have to force both querys with:
22 | // ready1 = sens1.hasValue(forceReading); //*
23 | // ready2 = sens2.hasValue(forceReading); //*
24 |
25 |
26 | #include
27 | #include
28 |
29 | hp_BH1750 sens1; // create first sensor
30 | hp_BH1750 sens2; // create second sensor
31 |
32 | unsigned int lux1, lux2; // hold the results
33 | bool ready1, ready2; // measurement finished?
34 | bool forceReading; // force readings and do not only wait for estimated measurement time.
35 |
36 |
37 | void setup() {
38 | forceReading = true; // also try false
39 | Serial.begin(9600);
40 | Serial.println("");
41 | sens1.begin(BH1750_TO_VCC); // adress pin is connected to VCC (5V or 3.3V)
42 | sens2.begin(BH1750_TO_GROUND); // adress pin is connected to ground
43 | sens1.calibrateTiming(); // Calibrate for fastest conversion time
44 | sens2.calibrateTiming(); // Calibrate for fastest conversion time
45 |
46 | sens1.start(BH1750_QUALITY_HIGH2, BH1750_MTREG_DEFAULT); // same settings for both sensors
47 | sens2.start(BH1750_QUALITY_HIGH2, BH1750_MTREG_DEFAULT);
48 | }
49 |
50 | void loop() {
51 | ready1 = sens1.hasValue(forceReading); //*
52 | ready2 = sens2.hasValue(forceReading); //*
53 | if ((ready1 == true) && (ready2 == true)) { //* when both seniors have completed their measurement,
54 | //if ((sens1.hasValue(forceReading) == true) && (sens2.hasValue(forceReading) == true)) { //**
55 |
56 | lux1 = sens1.getLux();
57 | lux2 = sens2.getLux();
58 | Serial.print(lux1);
59 | Serial.print("\t");
60 | Serial.println(lux2);
61 | sens1.start(); // start again after measuremnt is finished
62 | sens2.start();
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/keywords.txt:
--------------------------------------------------------------------------------
1 | hp_BH1750 KEYWORD1
2 | BH1750Quality LITERAL1
3 | BH1750_QUALITY_HIGH LITERAL1
4 | BH1750_QUALITY_HIGH2 LITERAL1
5 | BH1750_QUALITY_LOW LITERAL1
6 |
7 | BH1750CalResult LITERAL1
8 | BH1750_CAL_OK LITERAL1
9 | BH1750_CAL_MTREG_CHANGED LITERAL1
10 | BH1750_CAL_TOO_BRIGHT LITERAL1
11 | BH1750_CAL_TOO_DARK LITERAL1
12 | BH1750_CAL_COMMUNICATION_ERROR LITERAL1
13 |
14 | BH1750Timing LITERAL1
15 | mtregLow LITERAL1
16 | mtregHigh LITERAL1
17 | mtregLow_qualityHigh LITERAL1
18 | mtregHigh_qualityHigh LITERAL1
19 | mtregLow_qualityLow LITERAL1
20 | mtregHigh_qualityLow LITERAL1
21 |
22 | BH1750MtregLimit LITERAL1
23 | BH1750_MTREG_LOW LITERAL1
24 | BH1750_MTREG_HIGH LITERAL1
25 | BH1750_MTREG_DEFAULT LITERAL1
26 |
27 | BH1750Address LITERAL1
28 | BH1750_TO_GROUND LITERAL1
29 | BH1750_TO_VCC LITERAL1
30 |
31 | begin KEYWORD2
32 | powerOff KEYWORD2
33 | powerOn KEYWORD2
34 | reset KEYWORD2
35 |
36 | writeMtreg KEYWORD2
37 | setQuality KEYWORD2
38 | calibrateTiming KEYWORD2
39 | start KEYWORD2
40 | hasValue KEYWORD2
41 | processed KEYWORD2
42 | saturated KEYWORD2
43 | getLux KEYWORD2
44 | calcLux KEYWORD2
45 | luxFactor KEYWORD2
46 | setTiming KEYWORD2
47 | getTiming KEYWORD2
48 | getQuality KEYWORD2
49 | setTimeout KEYWORD2
50 | setTimeOffset KEYWORD2
51 | getTimeOffset KEYWORD2
52 | getTimeout KEYWORD2
53 | getMtreg KEYWORD2
54 | convertTimeToMtreg KEYWORD2
55 | getPercent KEYWORD2
56 | getRaw KEYWORD2
57 | getReads KEYWORD2
58 | getTime KEYWORD2
59 | getMtregTime KEYWORD2
60 | adjustSettings KEYWORD2
61 | calcSettings KEYWORD2
--------------------------------------------------------------------------------
/library.properties:
--------------------------------------------------------------------------------
1 | name=hp_BH1750
2 | version=1.0.2
3 | author=Stefan Armborst
4 | maintainer=Stefan Armborst
5 | sentence=Digital light sensor breakout boards containing the BH1750FVI IC
6 | paragraph=high performance non-blocking BH1750 library
7 | category=Sensors
8 | url=https://github.com/Starmbi/hp_BH1750
9 | architectures=*
10 |
--------------------------------------------------------------------------------
/src/hp_BH1750.cpp:
--------------------------------------------------------------------------------
1 | // This is a library for the light sensor BH1750
2 | // Datasheet https://www.mouser.com/datasheet/2/348/bh1750fvi-e-186247.pdf
3 | // Donwnload library at https://github.com/Starmbi/hp_BH1750
4 | // Help and infos are provided at https://github.com/Starmbi/hp_BH1750/wiki
5 | // Copyright (c) Stefan Armborst, 2020
6 |
7 | #include
8 | #include
9 |
10 | //********************************************************************************************
11 | // standard constructor
12 |
13 | hp_BH1750::hp_BH1750()
14 | {
15 | }
16 |
17 | //********************************************************************************************
18 | // Set address BH1750_TO_GROUND = 0x23 or BH1750_TO_VCC = 0x5C and connect sensor a compatible wire object
19 | // that can be one of the two wire objects of ESP 32 or a software wire object.
20 | // Set timing parameters to safe values as stated in datasheet
21 |
22 | bool hp_BH1750::begin(byte address, TwoWire *myWire)
23 | {
24 | _wire = myWire;
25 | _wire->begin(); // Initialisation of wire object with standard SDA/SCL lines
26 | _address = address; // Store one of the two available addresses
27 | _mtreg = BH1750_MTREG_DEFAULT; // Default sensitivity
28 | _quality = BH1750_QUALITY_HIGH2; // Sets quality to most sensitive mode (recommend, exept it is really bright)
29 | _qualFak = 0.5; // Is used for lux calculation (is 1 for BH1750_QUALITY_HIGH and BH1750_QUALITY_LOW)
30 | _offset = 0; // See "setOffset"
31 | _timeout = 10; // See "setTimeOut"
32 |
33 | _timing.mtregLow = BH1750_MTREG_LOW; // Use lowest sensitivity for calibrateTiming
34 | _timing.mtregHigh = BH1750_MTREG_HIGH; // .. and then use highest sensitivity for calibrateTiming
35 | _timing.mtregLow_qualityHigh = 81; // Most pessimistic timing data from datasheet
36 | _timing.mtregHigh_qualityHigh = 663;
37 | _timing.mtregLow_qualityLow = 11;
38 | _timing.mtregHigh_qualityLow = 89;
39 | return writeMtreg(BH1750_MTREG_DEFAULT); // Set standard sensitivity
40 | }
41 | //********************************************************************************************
42 | // Returns the current quality BH1750_QUALITY_HIGH = 0x20, BH1750_QUALITY_HIGH2 = 0x21, BH1750_QUALITY_LOW = 0x23
43 |
44 | BH1750Quality hp_BH1750::getQuality() const
45 | {
46 | return _quality;
47 | }
48 |
49 | //********************************************************************************************
50 | // Awakes the sensor from sleeping mode (only used before reset)
51 | // Is not neccessary before reading a value or starting measurement
52 |
53 | bool hp_BH1750::powerOn()
54 | {
55 | return writeByte(0x1);
56 | }
57 |
58 | //********************************************************************************************
59 | // For low power consuming. This mode is entered automatically if a measurement is finished
60 |
61 | bool hp_BH1750::powerOff()
62 | {
63 | return writeByte(0x0);
64 | }
65 |
66 | //********************************************************************************************
67 | // This sets the previous measurement result to zero (0)
68 | // Is only working after powerOn command (already included in this function)
69 |
70 | bool hp_BH1750::reset()
71 | {
72 | if (powerOn() == false)
73 | return false;
74 | return writeByte(0x7);
75 | }
76 |
77 | //********************************************************************************************
78 | // Private function. Sends command to sensor
79 |
80 | bool hp_BH1750::writeByte(byte b)
81 | {
82 | _wire->beginTransmission(_address);
83 | _wire->write(b);
84 | return (_wire->endTransmission() == 0);
85 | }
86 |
87 | //********************************************************************************************
88 | // Private function. Adjust mtreg to a valid range
89 |
90 | byte hp_BH1750::checkMtreg(byte mtreg)
91 | {
92 | if (mtreg < BH1750_MTREG_LOW)
93 | {
94 | mtreg = BH1750_MTREG_LOW;
95 | }
96 | if (mtreg > BH1750_MTREG_HIGH)
97 | {
98 | mtreg = BH1750_MTREG_HIGH;
99 | }
100 | return mtreg;
101 | }
102 |
103 | //********************************************************************************************
104 | // Start a single shot measurement with given mtreg and quality
105 |
106 | bool hp_BH1750::start()
107 | {
108 | reset(); // Reset the last result in data register to zero (0)
109 | bool result = writeByte(_quality);
110 | luxCache = (69.0 / _mtreg) * _qualFak;
111 | _startMillis = millis(); // Stores the start time
112 | _resultMillis = _startMillis + _mtregTime + _offset; // Add the pre-calculated conversion time to start time,
113 | _timeoutMillis = _startMillis + _mtregTime + _timeout; // to predict the time when conversion should be finished
114 | _nReads = 0; // Reset count for true readings to the sensor
115 | _value = 0; // Reset last result
116 | _time = 0; // Reset last measured conversion time
117 | _processed = false; // Value not readed by user
118 | return result;
119 | }
120 |
121 | //********************************************************************************************
122 | // Start measurement and set quality and sensitivity
123 | bool hp_BH1750::start(BH1750Quality quality, byte mtreg)
124 | {
125 | setQuality(quality);
126 | mtreg = checkMtreg(mtreg);
127 | if (mtreg != _mtreg)
128 | {
129 | _mtregTime = getMtregTime(mtreg);
130 | writeMtreg(mtreg); // Mtreg is only send to sensor if it is different from last measurement,
131 | } // because mtreg is stored in the chip
132 | return start();
133 | }
134 |
135 | //********************************************************************************************
136 | // Start 4 Measurements with different sensitivities and qualities and measure each conversion time
137 | // 2 measurements with low quality and 2 measurements with high quality
138 | // For each quality we use the lowest and highest sensitivity
139 | // This calibration needs about one second to execute
140 |
141 | byte hp_BH1750::calibrateTiming(byte mtregHigh, byte mtregLow)
142 | {
143 | // Store current values for restore if calibration failed
144 |
145 | BH1750Quality orgQ = _quality;
146 | byte orgM = _mtreg;
147 | BH1750Timing orgT = _timing;
148 | BH1750Timing nt;
149 |
150 | if (!begin(_address))
151 | {
152 | _timing = orgT;
153 | setQuality(orgQ);
154 | _mtreg = orgM;
155 | _mtregTime = getMtregTime();
156 | return BH1750_CAL_COMMUNICATION_ERROR; // Set timing to most pessimistic timing
157 | }
158 |
159 | _mtreg = 0; // A non existing value, to force a new calculation
160 | nt.mtregHigh = mtregHigh; // Set highest and lowest senitivity to the empty mtreg object
161 | nt.mtregLow = mtregLow;
162 | unsigned int time = readChange(nt.mtregHigh, BH1750_QUALITY_LOW, false); // Start measurement and return after conversation
163 | int newMtreg = mtregHigh; // Set highest sensitivity for first measurement
164 | if (_value > 0) // We found a valid value and can continue
165 | {
166 | if (_value == BH1750_SATURATED) // ..but unfortunately the light was to bright
167 | {
168 | nt.mtregLow_qualityLow = readChange(nt.mtregLow, BH1750_QUALITY_LOW, false); // ..so we measure at lowest sensitvity
169 | if (_value == BH1750_SATURATED)
170 | { // even lowest senitivity saturated = much to bright!
171 | _timing = orgT;
172 | writeMtreg(orgM);
173 | setQuality(orgQ);
174 | return BH1750_CAL_TOO_BRIGHT;
175 | }
176 | newMtreg = (float)BH1750_SATURATED / (_value * 1.1) * BH1750_MTREG_LOW + 0.5; // ..and calculate the highest possible sensitivity without saturation
177 | if (newMtreg > BH1750_MTREG_HIGH)
178 | newMtreg = BH1750_MTREG_HIGH;
179 | nt.mtregHigh = (byte)newMtreg;
180 | nt.mtregHigh_qualityLow = readChange(nt.mtregHigh, BH1750_QUALITY_LOW, false); // Now we repeat the measurement at high sensitivity with the adjusted mtreg
181 | } // saturated
182 | else
183 | { // not saturated
184 | nt.mtregHigh_qualityLow = time;
185 | // OK, first measurement was not saturated, so we continue
186 | // Here, for low sensitivity we DO NOT reset the sensor before measuring
187 | // With this trick we can even detect a value of zero what can easiely happen at low sensitvity
188 |
189 | nt.mtregLow_qualityLow = readChange(nt.mtregLow, BH1750_QUALITY_LOW, true);
190 | }
191 | // MtregHigh>0;
192 | // After measuring in Low-mode, we switch to high quality and repeat the two measurements with low and high sensitvity
193 |
194 | nt.mtregHigh_qualityHigh = readChange(nt.mtregHigh, BH1750_QUALITY_HIGH, false); // - 1;
195 | nt.mtregLow_qualityHigh = readChange(nt.mtregLow, BH1750_QUALITY_HIGH, true); // - 1;
196 | // Calibraton was succsessfull, so we update the new timing parameters to the sensor
197 | _timing = nt;
198 | writeMtreg(orgM);
199 | setQuality(orgQ);
200 | if (nt.mtregHigh == mtregHigh)
201 | {
202 | return BH1750_CAL_OK;
203 | }
204 | else
205 | {
206 | return BH1750_CAL_MTREG_CHANGED;
207 | }
208 | } // MtregHigh=0;
209 | // Calibration was too dark even for highest sensitivity
210 | // So we restore the last valid timing parameters and return false
211 | _timing = orgT;
212 | writeMtreg(orgM);
213 | setQuality(orgQ);
214 | return BH1750_CAL_TOO_DARK;
215 | }
216 |
217 | //********************************************************************************************
218 | // Private function
219 | // With the last parameter we can decide if we reset the sensor and look for values >0,
220 | // or if we let remain the last valid measurement in the storage of the sensor and look for a value<> last value
221 | // This function is only used for calibration, to detect even a value of zero (0)
222 | // This is a modified version from "startMeasure"
223 |
224 | unsigned int hp_BH1750::readChange(byte mtreg, BH1750Quality quality, bool change)
225 | {
226 | unsigned int curVal = 0;
227 | unsigned int val;
228 | if (change == true)
229 | {
230 | curVal = _value;
231 | }
232 | else
233 | {
234 | reset();
235 | }
236 | writeMtreg(mtreg);
237 | setQuality(quality);
238 | writeByte(quality);
239 | _startMillis = millis();
240 | _resultMillis = _startMillis + _mtregTime + _offset;
241 | _timeoutMillis = _startMillis + _mtregTime + _timeout;
242 | _nReads = 0;
243 | _value = 0;
244 | _time = 0;
245 | do
246 | {
247 | val = readValue();
248 | } while (val == curVal && millis() <= _timeoutMillis);
249 | _time = millis() - _startMillis;
250 | return _time;
251 | }
252 |
253 | //********************************************************************************************
254 | // Overloaded
255 | // Return the estimated time for a given combination of quality and sensitivity
256 | unsigned int hp_BH1750::getMtregTime(byte mtreg, BH1750Quality quality) const
257 | {
258 | if (quality == BH1750_QUALITY_LOW)
259 | {
260 | return (int)0.5 + _timing.mtregLow_qualityLow + ((_timing.mtregHigh_qualityLow - _timing.mtregLow_qualityLow) / (float)(_timing.mtregHigh - _timing.mtregLow)) * (mtreg - _timing.mtregLow);
261 | }
262 | else
263 | {
264 | return (int)0.5 + _timing.mtregLow_qualityHigh + ((_timing.mtregHigh_qualityHigh - _timing.mtregLow_qualityHigh) / (float)(_timing.mtregHigh - _timing.mtregLow)) * (mtreg - _timing.mtregLow);
265 | }
266 | }
267 |
268 | //********************************************************************************************
269 | // Overloaded
270 |
271 | unsigned int hp_BH1750::getMtregTime() const
272 | {
273 | return getMtregTime(_mtreg, _quality);
274 | }
275 |
276 | //********************************************************************************************
277 | // Overloaded
278 |
279 | unsigned int hp_BH1750::getMtregTime(byte mtreg) const
280 | {
281 | return getMtregTime(mtreg, _quality);
282 | }
283 |
284 | //********************************************************************************************
285 | // Return current sensitivity
286 |
287 | byte hp_BH1750::getMtreg() const
288 | {
289 | return _mtreg;
290 | }
291 |
292 | //********************************************************************************************
293 | // Return last conversation time
294 | unsigned int hp_BH1750::getTime() const
295 | {
296 | return _time;
297 | }
298 |
299 | //********************************************************************************************
300 | // Set quality for next measurements and adjust internal values
301 | void hp_BH1750::setQuality(BH1750Quality quality)
302 | {
303 | _quality = quality;
304 | if (_quality == BH1750_QUALITY_HIGH2)
305 | {
306 | _qualFak = 0.5; // For lux calculation
307 | }
308 | else
309 | {
310 | _qualFak = 1; // For lux calculation
311 | }
312 | _mtregTime = getMtregTime(); // Calculate estimated conversion time
313 | }
314 |
315 | //********************************************************************************************
316 | // Most used function in this library
317 | // In the main loop you should ask frequently with this function for a new value
318 | // If the estimated time for a new value not reached this function,
319 | // this function jumps back very quick, without ask the sensor
320 | // If forceSensor is true, then every time this function is called, the sensor is asked
321 | bool hp_BH1750::hasValue(bool forceSensor)
322 | {
323 | unsigned long mil = millis();
324 | if (!forceSensor)
325 | {
326 | if (_resultMillis > mil) // We are below the estimated time, so we return quickly
327 | {
328 | return false;
329 | }
330 | else
331 | { // Time is over
332 | _value = readValue();
333 | if (_value > 0)
334 | return true;
335 | if (_time > 0)
336 | return true; // Timeout
337 | return false; // Not timed out yet
338 | }
339 | }
340 | else
341 | { // forceSensor, so we always read from sensor
342 | _value = readValue();
343 | if (_value > 0)
344 | return true;
345 | if (_time > 0)
346 | return true; // Timeout
347 | return false; // Forced but not out-timed yet
348 | }
349 | }
350 |
351 | //********************************************************************************************
352 | // Here we can fine tune the estimated time
353 | // The offset can be negativ or positve
354 | // For example an offset of -2, reads 2 milli seconds earlier than estimated
355 |
356 | void hp_BH1750::setTimeOffset(int offset)
357 | {
358 | (_offset = offset);
359 | }
360 |
361 | //********************************************************************************************
362 | // Return the current offset
363 | int hp_BH1750::getTimeOffset() const
364 | {
365 | return _offset;
366 | }
367 |
368 | //********************************************************************************************
369 | // Set sensitivity and send it to the sensor
370 |
371 | bool hp_BH1750::writeMtreg(byte mtreg)
372 | {
373 | mtreg = checkMtreg(mtreg);
374 | _mtreg = mtreg;
375 | _mtregTime = getMtregTime();
376 | // Change sensitivity measurement time
377 | // We send two bytes: 3 Bits und 5 Bits, with a prefix.
378 | // High bit: 01000_MT[7,6,5]
379 | // Low bit: 011_MT[4,3,2,1,0]
380 | uint8_t hiByte = _mtreg >> 5;
381 | hiByte |= 0b01000000;
382 | writeByte(hiByte);
383 | uint8_t loByte = mtreg & 0b00011111;
384 | loByte |= 0b01100000;
385 | return writeByte(loByte);
386 | }
387 |
388 | //********************************************************************************************
389 | // Return the physically reads form the sensor
390 |
391 | unsigned int hp_BH1750::getReads() const
392 | {
393 | return _nReads;
394 | }
395 |
396 | //********************************************************************************************
397 | // Return a value between 0 65535 (two bytes)
398 |
399 | unsigned int hp_BH1750::getRaw()
400 | {
401 | if (_time > 0)
402 | return _value;
403 | yield();
404 | do
405 | {
406 | _value = readValue();
407 | } while (_time == 0);
408 | _processed = true;
409 | return _value;
410 | }
411 |
412 | bool hp_BH1750::processed() const
413 | {
414 | return _processed;
415 | }
416 |
417 | //********************************************************************************************
418 | // Private function that reads the value physically
419 |
420 | unsigned int hp_BH1750::readValue()
421 | {
422 | byte buff[2];
423 | unsigned int req = _wire->requestFrom((int)_address, (int)2); // request two bytes
424 | if (req < 2 || _wire->available() < 2)
425 | {
426 | _time = 999;
427 | _value = 0;
428 | return _value; // Sensor not found or other problem
429 | }
430 |
431 | buff[0] = _wire->read(); // Receive one byte
432 | buff[1] = _wire->read(); // Receive one byte
433 | _nReads++; // Inc the physically count of reads
434 | _value = ((buff[0] << 8) | buff[1]);
435 |
436 | unsigned long mil = millis();
437 | if (_value > 0 && _time == 0)
438 | _time = mil - _startMillis; // Conversion time
439 | if (mil >= (_timeoutMillis) && (_time == 0))
440 | _time = mil - _startMillis;
441 | return _value;
442 | }
443 |
444 | //********************************************************************************************
445 | // Get the current timeout in milliseconds
446 |
447 | unsigned int hp_BH1750::getTimeout() const
448 | {
449 | return _timeout;
450 | }
451 |
452 | void hp_BH1750::setTimeout(int timeout)
453 | {
454 | _timeout = timeout;
455 | }
456 |
457 | //********************************************************************************************
458 | // Return all timing parameters, collected in a struct
459 |
460 | BH1750Timing hp_BH1750::getTiming() const
461 | {
462 | return _timing;
463 | }
464 |
465 | //********************************************************************************************
466 | // Set all timing parameters (for example stored in eprom before)
467 |
468 | void hp_BH1750::setTiming(BH1750Timing timing)
469 | {
470 | _timing = timing;
471 | }
472 |
473 | //********************************************************************************************
474 | // If you want to measure a certain time in millisconds, this funcion calculates the right mtreg
475 | // Only vaild result, if sensor is calibrated!
476 |
477 | byte hp_BH1750::convertTimeToMtreg(unsigned int time, BH1750Quality quality)
478 | {
479 | float v;
480 | switch (quality)
481 | {
482 | case BH1750_QUALITY_HIGH:
483 | case BH1750_QUALITY_HIGH2:
484 | v = (((float)time - (float)_timing.mtregLow_qualityHigh) * (_timing.mtregHigh - _timing.mtregLow)) / (_timing.mtregHigh_qualityHigh - _timing.mtregLow_qualityHigh) + _timing.mtregLow;
485 | break;
486 | case BH1750_QUALITY_LOW:
487 | default:
488 | v = ((time - _timing.mtregLow_qualityLow) * (_timing.mtregHigh - _timing.mtregLow)) / (_timing.mtregHigh_qualityLow - _timing.mtregLow_qualityLow) + _timing.mtregLow;
489 | break;
490 | }
491 | v += 0.5;
492 | byte tempMtreg;
493 | Serial.println(v);
494 | if (v < 0)
495 | tempMtreg = 0;
496 | else if (v > BH1750_MTREG_HIGH)
497 | tempMtreg = 255;
498 | else
499 | tempMtreg = v;
500 | // if (v < BH1750_MTREG_LOW) tempMtreg = 0;
501 | // if (v > BH1750_MTREG_HIGH) tempMtreg = 255;
502 | // if (v >=BH1750_MTREG_LOW && v <= BH1750_MTREG_HIGH) tempMtreg = v;
503 |
504 | return tempMtreg;
505 | }
506 |
507 | //********************************************************************************************
508 | // Return the light value, converted to Lux
509 |
510 | float hp_BH1750::getLux()
511 | {
512 | return (float)getRaw() / luxFactor * luxCache;
513 | }
514 |
515 | //********************************************************************************************
516 | // Calculate a given digital value (from getRaw()) to Lux
517 |
518 | float hp_BH1750::calcLux(int raw) const
519 | {
520 | return (float)raw / luxFactor * _qualFak * 69 / _mtreg;
521 | }
522 |
523 | // Calculate a given digital value (from getRaw()) to Lux
524 |
525 | float hp_BH1750::calcLux(int raw, BH1750Quality quality, int mtreg) const
526 | {
527 | float qualFak = 0.5;
528 | if (quality != BH1750_QUALITY_HIGH2)
529 | qualFak = 1.0;
530 | return (float)raw / luxFactor * qualFak * 69 / mtreg;
531 | }
532 |
533 | // Check if last value is over 65535 raw data
534 |
535 | bool hp_BH1750::saturated() const
536 | {
537 | return (_value == BH1750_SATURATED);
538 | }
539 |
540 | //********************************************************************************************
541 | // This is a function that you can call after you received a value
542 | // Depending of the last value, this function adjusts the mtreg and the quality so that
543 | // the next measurement with similar light conditions is in the desired range
544 | // The desired range is set with the parameter "percent"
545 | // If you set it to 50% the next raw data should be near to 50% of the maximum digits of 65535=saturated, this is 32768
546 | // Rf the light condition changes you have enough buffer that you are not out of range (saturated)
547 | // If you want the highest precision you should choose a percentage of 90-95%
548 | // So it is more likley that the next value could be saturated, but you have a better resolution
549 | // If the next value is saturated, this function does a quick measurement at low resolution (~10 ms)
550 | // and calculates suitable parameters for the next measurement
551 | // With this function you cover the whole sensitivity of the sensor without manually adjust any paramaeters!
552 | // If you start the measurement with quality BH1750_QUALITY_HIGH = 0x20 or BH1750_QUALITY_HIGH2 = 0x21 the function will
553 | // switch between this values when requiered
554 | // If you use the fast, but low qualtiy BH1750_QUALITY_LOW = 0x23, the function will not change this quality.
555 |
556 | bool hp_BH1750::adjustSettings(float percent, bool forcePreShot)
557 | {
558 | if (_value == BH1750_SATURATED || forcePreShot == true) // If last result is saturated, perfom a measurement at low sensitivity
559 | {
560 | BH1750Quality temp = _quality;
561 | start(BH1750_QUALITY_LOW, BH1750_MTREG_LOW);
562 | getRaw();
563 | if (temp != BH1750_QUALITY_LOW) _quality = BH1750_QUALITY_HIGH;
564 | }
565 | calcSettings(_value, _quality, _mtreg, percent);
566 | setQuality(_quality);
567 | return writeMtreg(_mtreg);
568 | }
569 |
570 | //********************************************************************************************
571 | // This function is like adjustSettings(), but only calculates the new settings without send them to the sensor
572 | // You have to declare the variables BH1750Quality and mtreg bevore calling this function
573 | // After calling this function this variables contain the appropiate values
574 |
575 | void hp_BH1750::calcSettings(unsigned int value, BH1750Quality &qual, byte &mtreg, float percent)
576 | {
577 | if (percent > 100.0)
578 | percent = 100.0;
579 | _percent = percent;
580 | if (value==0)
581 | value=1;
582 | float highBound = value / percent * 100.0;
583 | unsigned long newMtreg = (float)BH1750_SATURATED / highBound * mtreg + .5;
584 | switch (qual)
585 | {
586 | case BH1750_QUALITY_HIGH:
587 | if (newMtreg >= BH1750_MTREG_LOW * 2)
588 | {
589 | newMtreg /= 2;
590 | qual = BH1750_QUALITY_HIGH2;
591 | }
592 | break;
593 | case BH1750_QUALITY_HIGH2:
594 | if (newMtreg < BH1750_MTREG_LOW)
595 | {
596 | qual = BH1750_QUALITY_HIGH;
597 | newMtreg *= 2;
598 | }
599 | case BH1750_QUALITY_LOW:
600 | default: ;
601 | }
602 | if (newMtreg > BH1750_MTREG_HIGH)
603 | newMtreg = BH1750_MTREG_HIGH;
604 | mtreg = newMtreg;
605 | }
606 |
607 | float hp_BH1750::getPercent() const {
608 | return _percent;
609 | }
610 |
--------------------------------------------------------------------------------
/src/hp_BH1750.h:
--------------------------------------------------------------------------------
1 | // This is a library for the light sensor BH1750
2 | // Datasheet https://www.mouser.com/datasheet/2/348/bh1750fvi-e-186247.pdf
3 | // Donwnload library at https://github.com/Starmbi/hp_BH1750
4 | // Help and infos are provided at https://github.com/Starmbi/hp_BH1750/wiki
5 | // Copyright (c) Stefan Amrborst, 2020
6 |
7 | #ifndef hp_BH1750_h
8 | #define hp_BH1750_h
9 | #if defined(ARDUINO) && ARDUINO >= 100
10 | #include
11 | #else
12 | #include
13 | #endif
14 | #include
15 | static const unsigned int BH1750_SATURATED = 65535;
16 | enum BH1750Quality
17 | {
18 | BH1750_QUALITY_HIGH = 0x20,
19 | BH1750_QUALITY_HIGH2 = 0x21,
20 | BH1750_QUALITY_LOW = 0x23,
21 | };
22 | enum BH1750CalResult
23 | {
24 | BH1750_CAL_OK = 0,
25 | BH1750_CAL_MTREG_CHANGED = 1,
26 | BH1750_CAL_TOO_BRIGHT = 2,
27 | BH1750_CAL_TOO_DARK = 3,
28 | BH1750_CAL_COMMUNICATION_ERROR = 4,
29 | };
30 | struct BH1750Timing
31 | {
32 | byte mtregLow;
33 | byte mtregHigh;
34 | unsigned int mtregLow_qualityHigh;
35 | unsigned int mtregHigh_qualityHigh;
36 | unsigned int mtregLow_qualityLow;
37 | unsigned int mtregHigh_qualityLow;
38 | };
39 |
40 | enum BH1750MtregLimit
41 | {
42 | BH1750_MTREG_LOW = 31, //the datashet specifies 31 as minimum value
43 | //but you can go even lower (depending on your specific chip)
44 | //BH1750_MTREG_LOW=5 works with my chip and enhances the range
45 | //from 121.556,8 Lux to 753.652,5 Lux.
46 | BH1750_MTREG_HIGH = 254,
47 | BH1750_MTREG_DEFAULT = 69
48 | };
49 |
50 | enum BH1750Address
51 | {
52 | BH1750_TO_GROUND = 0x23,
53 | BH1750_TO_VCC = 0x5C
54 | };
55 | extern TwoWire Wire; /**< Forward declaration of Wire object */
56 | class hp_BH1750
57 | {
58 | public:
59 | hp_BH1750();
60 |
61 | //hp_BH1750();
62 |
63 | bool begin(byte address, TwoWire *myWire = &Wire);
64 | bool reset();
65 | bool powerOn();
66 | bool powerOff();
67 | bool writeMtreg(byte mtreg);
68 | void setQuality(BH1750Quality quality);
69 | byte calibrateTiming(byte mtregHigh = BH1750_MTREG_HIGH, byte mtregLow = BH1750_MTREG_LOW);
70 | bool start();
71 | bool start(BH1750Quality quality, byte mtreg);
72 | bool hasValue(bool forceSensor = false);
73 | bool processed() const;
74 | bool saturated() const;
75 | float getLux();
76 | float calcLux(int raw) const;
77 | float calcLux(int raw, BH1750Quality quality, int mtreg) const;
78 | float luxFactor = 1.2;
79 | void setTiming(BH1750Timing timing);
80 | BH1750Timing getTiming() const;
81 |
82 | BH1750Quality getQuality() const;
83 |
84 | void setTimeout(int timeout);
85 |
86 | void setTimeOffset(int offset);
87 | int getTimeOffset() const;
88 | unsigned int getTimeout() const;
89 | byte getMtreg() const;
90 | byte convertTimeToMtreg(unsigned int time, BH1750Quality quality);
91 | float getPercent() const;
92 | unsigned int getRaw();
93 | unsigned int getReads() const;
94 | unsigned int getTime() const;
95 | unsigned int getMtregTime() const;
96 | unsigned int getMtregTime(byte mtreg) const;
97 | unsigned int getMtregTime(byte mtreg, BH1750Quality quality) const;
98 |
99 | bool adjustSettings(float percent = 50.0, bool forcePreShot = false);
100 | void calcSettings(unsigned int value, BH1750Quality &qual, byte &mtreg, float percent);
101 |
102 | private:
103 | TwoWire *_wire;
104 | byte _address;
105 | byte _mtreg;
106 | byte _percent=50;
107 | bool _processed = false;
108 | unsigned int _mtregTime;
109 | unsigned long _startMillis;
110 | unsigned long _resultMillis;
111 | unsigned long _timeoutMillis;
112 | unsigned long _timeout = 10;
113 | int _offset = 0;
114 | unsigned int _nReads;
115 | unsigned int _time;
116 | unsigned int _value;
117 | float _qualFak = 0.5;
118 | float luxCache;
119 | BH1750Quality _quality;
120 | BH1750Timing _timing;
121 |
122 | byte checkMtreg(byte mtreg);
123 | bool writeByte(byte b);
124 |
125 | unsigned int readValue();
126 | unsigned int readChange(byte mtreg, BH1750Quality quality, bool change);
127 | };
128 | #endif
129 |
--------------------------------------------------------------------------------