Самодельный измеритель освещённости (люксметр) на bh1750, arduino и nokia 5110

Содержание

Подключение BH1750 к Arduino

Модуль модуль GY-302 оборудован пяти-пиновым разъемом стандарта 2.54мм:

  • VCC: Питание «+»
  • GND: Земля «-«
  • SCL: Линия тактирования (Serial CLock)
  • SDA: Линия данных (Serial Data)
  • ADDR: Выбор адреса

Выводы отвечающие за интерфейс I2C на платах Arduino на базе различных контроллеров разнятся:

Arduino Mega Arduino Uno/Nano/Pro Mini BH1750 модуль Цвет проводов на фото
GND GND GND Черный
5V 5V VCC Красный
20 (SDA) A4 SDA Синий
21 (SCL) A5 SCL Зелёный
3.3V 3.3V ADDR Жёлтый

Схема подключения BH1750 к Arduino по I2C

На следующем рисунке показана схема подключения датчика внешней освещенности BH1750 к Arduino UNO. Вывод ADD можно оставить «висящим»:

но вы можете подключить его к 3.3 В. Это переведет вывод ADD в высокий логический уровень, и адрес ведомого I2C датчика внешней освещенности BH1750 станет 0x5C

Это важно в программировании. Если вывод ADD переведен в низкое логическое состояние путем подключения к земле, адрес ведомого устройства I2C датчика внешней освещенности BH1750 будет 0x23

Таким образом, два датчика внешней освещенности BH1750 могут быть подключены к одной шине I2C, где один вывод ADD имеет низкое логическое состояние, а другой вывод ADD высокое.

Пример скетча

В скетче мы каждые 1000 мсек считываем с датчика BH1750 показания освещённости в люксах и выводим эти данные в последовательный порт.

/*
Подключяем библиотеку Wire (для I2C)
*/
#include <Wire.h>
/*
Подключяем библиотеку для bh1750
*/
#include <BH1750.h>
/*
Объявляем объект lightMeter
*/
BH1750 lightMeter;

void setup() {
/*
Запускаем последовательный порт
*/
Serial.begin(9600);
/*
Инициализируем шину I2C (библиотека BH1750 не делает это автоматически)
На esp8266 вы можете выбрать выводы SCL и SDA, используя Wire.begin (D4, D3);
*/
Wire.begin();
/*
Инициализируем и запускаем BH1750
*/
lightMeter.begin();

Serial.println(F(«BH1750 тест»));
}

void loop() {
/*
Считываем показания с BH1750
*/
float lux = lightMeter.readLightLevel();
/*
Отправляем значение освещенности в последовательный порт
*/
Serial.print(«Light: «);
Serial.print(lux);
Serial.println(» lx»);
/*
раз в секунду
*/
delay(1000);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

/*
   Подключяем библиотеку Wire (для I2C)
*/
#include <Wire.h>
/*
   Подключяем библиотеку для bh1750
*/
#include <BH1750.h>
/*
   Объявляем объект lightMeter
*/

BH1750lightMeter;

voidsetup(){

/*

     Запускаем последовательный порт
  */

Serial.begin(9600);

/*

    Инициализируем шину I2C (библиотека BH1750 не делает это автоматически)
    На esp8266 вы можете выбрать выводы SCL и SDA, используя Wire.begin (D4, D3);
  */

Wire.begin();

/*

    Инициализируем и запускаем BH1750
  */

lightMeter.begin();

Serial.println(F(«BH1750 тест»));

}
 

voidloop(){

/*

    Считываем показания с BH1750
  */

floatlux=lightMeter.readLightLevel();

/*

    Отправляем значение освещенности в последовательный порт
  */

Serial.print(«Light: «);

Serial.print(lux);

Serial.println(» lx»);

/*

     раз в секунду
  */

delay(1000);

}

Результат

Открыть монитор последовательного порта можно сочетанием клавиш Ctrl+Shift+M или через меню Инструменты. В мониторе последовательного порта побегут значения освещённости с нашего датчика BH1750.

Overview

The BH1750 has six different measurement modes which are divided in two groups;
continuous and one-time measurements. In continuous mode the sensor
continuously measures lightness value. In one-time mode, the sensor makes only
one measurement and then goes into Power Down mode.

Each mode has three different precisions:

  • Low Resolution Mode — (4 lx precision, 16ms measurement time)
  • High Resolution Mode — (1 lx precision, 120ms measurement time)
  • High Resolution Mode 2 — (0.5 lx precision, 120ms measurement time)

By default, this library uses Continuous High Resolution Mode, but you can
change this to a different mode by passing the mode argument to
BH1750.begin().

When the One-Time mode is used your sensor will go into Power Down mode when
it completes the measurement and you’ve read it. When the sensor is powered up
again it returns to the default mode which means it needs to be reconfigured
back into One-Time mode. This library has been implemented to automatically
reconfigure the sensor when you next attempt a measurement so you should not
have to worry about such low level details.

Usually you will get an integer value which represent the lux equivalent.

  • Low Resolution Mode — (generic range: 0.0 up to 54612.5 lux)
  • High Resolution Mode — (generic range: 0.0 up to 54612.5 lux)
  • High Resolution Mode 2 — (generic range: 0.0 up to 27306.25 lux)

The sensor itself returns a 16 bit unsigned integer. Therefore the maximum value is limited in general.
The standard conversion between the so called ‘counts’ to lux is 1/1.2, that means you get a smaller value.
As we use float, if an error occurs you will get a negative value.

  • -1 no valid data was transmitted from the sensor
  • -2 device is not configured
    Otherwise the measured counts are converted to lux and returned. If no advanced parameters are changed the maximum lux value is 54612.5 lx.

As the sensor counts impact of light in a specific time frame you could change this time frame.
This is needed if you use an overlay window or compensate for environmental influence like darkness.
This time frame is defined by a register which is called MTreg. Therefore you could choose a value between 32 and 254.
The default value is 69; keep in mind that the measurement time is changed accordingly.

How to use the BH1750 library

The BH1750 library is used very similar to the BME280 library (or BMP180). At the beginning of the program, the library is called and the lightMeter object is initialized by indicating the address of the BH1750 on the I2C bus. By default the BH1750 is located at address 0x23. If you have a conflict with another component, you can assign the address 0x5C by feeding the addr pin to 3.3V.

The library supports the 6 modes of operation of the sensor. The sensor can measure continuous brightness

  • BH1750_CONTINUOUS_LOW_RES_MODE: Fast measurement (16ms) at low resolution (4 lux of precision)
  • BH1750_CONTINUOUS_HIGH_RES_MODE (default mode): High resolution (1 lux accuracy). The measurement time is 120ms
  • BH1750_CONTINUOUS_HIGH_RES_MODE_2: Very high resolution (0.5 lux accuracy). Measurement time 120ms

These three other modes allow to realize a single measurement (One_Time) and then to put the sensor in energy saving. Accuracy and measurement time are identical.

  • BH1750_ONE_TIME_LOW_RES_MODE
  • BH1750_ONE_TIME_HIGH_RES_MODE
  • BH1750_ONE_TIME_HIGH_RES_MODE_2

In the setup, the lightMeter object is started by using the function begin (uint8_t mode) by passing it as parameter the measurement mode. The configure (uint8_t mode) function is (called by begin) is also exposed.

The readLightLevel method reads the light intensity measured by the BH1750 at any time. The function returns the measurement directly to Lux.

Сравнение BH1750 и TSL2561

Спектр видимого света колеблется от 380 нм (фиолетовый) до 780 нм (красный) (в вакууме). Короче говоря, BH1750 просто измеряет это. TSL2561, помимо видимого спектра, также измеряет инфракрасное излучение. Он имеет два диода, один для видимого, а второй для инфракрасного. На мой взгляд, BH1750 соответствует большинству требований, тогда как TSL2561 предлагает более широкий спектр, большую чувствительность и большую точность.

Для сравнения, оба датчика были размещены параллельно в макете, один рядом с другим. Таким образом, можно получить практически одинаковое излучение на оба датчика.

Более подробно о том, как работать с TSL2561 в Ардуино, можете найти в отдельной статье Подключение цифрового датчика освещенности TSL2561 к Arduino.

На следующем рисунке показана схема подключения датчиков внешней освещенности TSL2561 и BH1750 к Arduino UNO.

Пример скетча

#include <Wire.h>
#include <BH1750.h>
#include <Adafruit_TSL2561_U.h>

BH1750 bh1750;
Adafruit_TSL2561_Unified tsl2561 = Adafruit_TSL2561_Unified(TSL2561_ADDR_FLOAT, 12345);

void setup() {
Serial.begin(9600);
Wire.begin();

/*
TSL2561
*/
if (!tsl2561.begin()) {
Serial.print(F(«При обнаружении TSL2561 возникла проблема … проверьте подключение!»));
while (1);
}

tsl2561.setGain(TSL2561_GAIN_1X);
tsl2561.setIntegrationTime(TSL2561_INTEGRATIONTIME_402MS);

/*
BH1750
*/
bh1750.begin(BH1750::ONE_TIME_HIGH_RES_MODE);

Serial.println(F(«BH1750 vs TSL2561»));
}

void loop() {
/*
Считываем показания с TSL2561
*/
sensors_event_t event;
tsl2561.getEvent(&event);
/*
Считываем показания с BH1750
*/
float lux = bh1750.readLightLevel();
/*
Отправляем значение освещенности в последовательный порт
*/
if (event.light) {
Serial.print(F(«TSL2561: «));
Serial.print(event.light);
Serial.println(F(» lx»));
} else {
/*
Если event.light = 0 люкса датчик, вероятно, насыщенные
и достоверные данные не может быть сгенерированы!
*/
Serial.println(F(«Насыщенные TSL2561»));
}
Serial.print(F(«BH1750 : «));
Serial.print(lux);
Serial.println(F(» lx»));

delay(1000);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

#include <Wire.h>
#include <BH1750.h>
#include <Adafruit_TSL2561_U.h>
 

BH1750bh1750;

Adafruit_TSL2561_Unifiedtsl2561=Adafruit_TSL2561_Unified(TSL2561_ADDR_FLOAT,12345);

voidsetup(){

Serial.begin(9600);

Wire.begin();

/*

     TSL2561
  */

if(!tsl2561.begin()){

Serial.print(F(«При обнаружении TSL2561 возникла проблема … проверьте подключение!»));

while(1);

}

tsl2561.setGain(TSL2561_GAIN_1X);

tsl2561.setIntegrationTime(TSL2561_INTEGRATIONTIME_402MS);

/*

    BH1750
  */

bh1750.begin(BH1750::ONE_TIME_HIGH_RES_MODE);

Serial.println(F(«BH1750 vs TSL2561»));

}
 

voidloop(){

/*

    Считываем показания с TSL2561
  */

sensors_event_tevent;

tsl2561.getEvent(&event);

/*

    Считываем показания с BH1750
  */

floatlux=bh1750.readLightLevel();

/*

    Отправляем значение освещенности в последовательный порт
  */

if(event.light){

Serial.print(F(«TSL2561: «));

Serial.print(event.light);

Serial.println(F(» lx»));

}else{

/*

      Если event.light = 0 люкса датчик, вероятно, насыщенные
      и достоверные данные не может быть сгенерированы!
    */

Serial.println(F(«Насыщенные TSL2561»));

}

Serial.print(F(«BH1750 : «));

Serial.print(lux);

Serial.println(F(» lx»));

delay(1000);

}

Результат

По результатам TSL2561 и BH1750 дают практически идентичные показатели в одинаковых условиях. Это говорит о том, что сами датчики идут откалиброванными с завода.

Example

An example using the BH1750 library in conjunction with the GY-30 board
(which contains the BH1750 component) is presented below. The example
code uses the BH1750 library in the default continuous high precision
mode when making light measurements.

Wiring

Connections:

  • VCC -> 3V3 or 5V
  • GND -> GND
  • SCL -> SCL (A5 on Arduino Nano, Uno, Leonardo, etc or 21 on Mega and Due, on esp8266 free selectable)
  • SDA -> SDA (A4 on Arduino Nano, Uno, Leonardo, etc or 20 on Mega and Due, on esp8266 free selectable)
  • ADD -> NC/GND or VCC (see below)

The ADD pin is used to set the sensor I2C address. By default (if ADD voltage
less than 0.7 * VCC) the sensor address will be 0x23. If it has voltage
greater or equal to 0.7VCC voltage (e.g. you’ve connected it to VCC) the
sensor address will be 0x5C.

Wiring up the GY-30 sensor board to an Arduino is shown in the diagram below.

.

Code

Upload the BH1750 test code to your Arduino.

#include <Wire.h>
#include <BH1750.h>

BH1750 lightMeter;

void setup(){

  Serial.begin(9600);

  // Initialize the I2C bus (BH1750 library doesn't do this automatically)
  // On esp8266 devices you can select SCL and SDA pins using Wire.begin(D4, D3);
  Wire.begin();

  lightMeter.begin();
  Serial.println(F("BH1750 Test"));

}

void loop() {

  float lux = lightMeter.readLightLevel();
  Serial.print("Light: ");
  Serial.print(lux);
  Serial.println(" lx");
  delay(1000);

}

More Examples

The directory contains more advanced use cases such as using different modes, I2C addresses and multiple Wire instances.

gbj_bh1750()

Description

The library does not need special constructor and destructor, so that the inherited ones are good enough and there is no need to define them in the library, just use it with default or specific parameters as defined at constructor of parent library .

  • Constructor sets parameters specific to the two-wire bus in general.
  • All the constructor parameters can be changed dynamically with corresponding setters later in a sketch.

Parameters

  • clockSpeed: Two-wire bus clock frequency in Hertz. If the clock is not from enumeration, it fallbacks to 100 kHz.
    • Valid values: gbj_bh1750::CLOCK_100KHZ, gbj_bh1750::CLOCK_400KHZ
    • Default value: gbj_bh1750::CLOCK_100KHZ
  • busStop: Logical flag about releasing bus after end of transmission.
    • Valid values: true, false
      • true: Releases the bus after data transmission and enables other master devices to control the bus.
      • false: Keeps connection to the bus and enables to begin further data transmission immediately.
    • Default value: true
  • pinSDA: Microcontroller’s pin for serial data. It is not a board pin but GPIO number. For hardware two-wire bus platforms it is irrelevant and none of methods utilizes this parameter for such as platforms for communication on the bus. On the other hand, for those platforms the parameters might be utilized for storing some specific attribute in the class instance object.
    • Valid values: positive integer
    • Default value: 4 (GPIO4, D2)
  • pinSCL: Microcontroller’s pin for serial clock. It is not a board pin but GPIO number. For hardware two-wire bus platforms it is irrelevant and none of methods utilizes this parameter for such as platforms. On the other hand, for those platforms the parameters might be utilized for storing some specific attribute in the class instance object.
    • Valid values: positive integer
    • Default value: 5 (GPIO5, D1)

Returns

Object performing the sensor management.
The constructor cannot return directly, however, it stores them in the instance object. The result can be tested in the operational code with the method , , or .

Example

The method has all arguments defaulted and calling without any parameters is equivalent to the calling with all arguments set by corresponding constant with default value:

  gbj_bh1750 Sensor = gbj_bh1750(); // It is equivalent to
  gbj_bh1750 Sensor = gbj_bh1750(gbj_bh1750::CLOCK_100KHZ, true, D2, D1);

Developers

The following information is for developers of this library.

Code Format

The code in this project is formatted using tool.

Once the tool has been install you can then run the
convenience script () to check or apply the code
formatting. The script should be run from the repo’s top level directory.

$ ./ci/code-format.bash

This script is also run as part of the project’s continuous integration
checks.

If you make changes to code files then the code format can be applied
by simply passing apply as an argument to the script.

$ ./ci/code-format.bash apply

Code Linting

To run the linter over the project use the command below.

$ arduino-lint --library-manager update --compliance strict

The same command is run as part of the project’s continuous integration
checks.

If errors or warnings are reported then fix them and re-run the script
until they are resolved.

Build Locally

The code in this project can be built locally using the tool.
The tool can be installed using the instructions . Once you have the tool installed you can compile the
examples scripts using the convenience script ().

$ ./ci/compile-examples.bash

This script performs the same actions as the project’s continuous integration
compile checks.

Release Process

  • Update and version strings.
  • Create a new project release and use the new version number as tag. Click Publish.
  • Now wait about an hour for it to appear in the Arduino library manager.

How to measure the quality of lighting?

In Europe, EN 12464-1 (summary in English and French) defines the minimum lighting levels according to the occupied workplace.

Source: LUX Lighting Review No. 228 May / June 2004 available online.

In a dwelling, there is no specific standard (to my knowledge). Keria, a lighting specialist, has published some common light intensities on his site. Here are excerpts of the recommendations for some rooms of the house (or to obtain a certain atmosphere: intimate, convivial, game, work).

Pièce et ambiance recherchée Intensité lumineuse Couleur recommandée
Salon, salle à manger, chambre d’adulte. Ambiance tamisée, intime. 25-50 lux Blanc chaud
Salon, salle à manger, cuisine, chambre d’adulte, bureau. Ambiance conviviale, moments de détente. 100-150 lux Blanc chaud
Bureau, bibliothèque, chambre d’enfant, salle de jeux. Ambiance de travail, de jeux. 200-250 lux Blanc neutre
Ambiance technique : espaces de forte activité (bureau, atelier) et de circulation (couloirs, entrée) 350-500 lux Blanc froid

Source : https://www.keria.com/guides/comment-calculer-la-quantite-declairage-necessaire-dans-une-piece

On the basis of these different data, I constructed a 5-level indicator (too low, low, ideal, high, too high). You can adjust the values according to your habits and needs.

#define _TOOLOW                       25
#define _LOW                          50
#define _HIGH                         500
#define _TOOHIGH                      750
#define LEVEL_TOOLOW                  "Too low"
#define LEVEL_LOW                     "Low"
#define LEVEL_OPTIMAL                 "Ideal"
#define LEVEL_HIGH                    "High"
#define LEVEL_TOOHIGH                 "Too high"

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 instead or additional to .

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 » to quality .
This doubles your range to 121557 Lux, but also halves the resolution.

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

With quality you will read raw data like: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, …
With quality 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 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, %.

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: Here the declaration of the function:

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

Конструктив

Изготовление корпуса

Размечаю окно под дисплей и дырки под кнопки

Делаю отверстия и ровняю из гравером

Примеряю плату с деталями

Подгоняю, проверяю отверстия

Батарейный отсек

Изготавливаю из старой заглушки от системного блока. Размягчаю феном, подгоняю под аккумулятор и вставляю две пружинки — контакты аккумулятора

Припаиваю провода к контактам и креплю батарейный отсек в корпусе на «холодную сварку»

После высыхания этого «чудо пластилина» получаю вполне надежное крепление аккумулятора в корпусе с возможностью его быстрого извлечения для зарядки

Размещаю все компоненты на макетной плате

И распаиваю согласно схемы любимыми тефлоновыми проводами

Сенсор освещенности

Под рукой оказалась прозрачная коробочка от SD-карточки. Поместил модуль освещенности пока туда, хотя выглядит достаточно неказисто. На отрезке гибкого 4-х жильного телефонного провода обжал разъемчики в стиле Ардуино

Собираю все вместе

Верхняя крышка

Рисую в графическом пакете макет надписей на верхнюю крышку и печатаю в зеркальном виде на прозрачную пленку, а затем приклеиваю ее к крышке

И вот готовый вид прибора

4Взаимодействие с сенсором BH1750 в деталях

Для управления датчиком мы должны по его I2C адресу записать код команды. Под управлением понимается изменение режима работы, изменение чувствительности, сброс и т.д. Команды можно передавать только по одной. Для чтения мы должны запросить из датчика 2 байта. Датчик хранит всегда значение в виде 16-битного числа, но его интерпретация зависит от текущей чувствительности.

Последовательность обмена с датчиком BH1750

На следующем рисунке представлен список команд, необходимых для работы с датчиком BH1750:

Команды управления датчиком BH1750

Как мы знаем, управление и обмен данными с датчиком BH1750 происходит по протоколу I2C. Мне нравится использовать для быстрых тестов общения с устройствами I2C и SPI отладочную плату с микросхемой FT2232H. Подключим SCL датчика к SCL микросхемы FT2232H, SDA датчика – к SDA и SDO микросхемы. Питание 5В возьмём с Arduino Nano, а все земли объединим.

Модуль с датчиком BH1750 управляется микросхемой FTDI

Теперь запустим программу SPI via FTDI и попробуем прочитать значения, хранящиеся в регистрах сенсора.

Чтение единичного показания датчика BH1750 с помощью FT2232HНепрерывное считывание показаний датчика BH1750 с помощью FT2232H

Что такое экспонометр?

В этом уроке я покажу вам, как сделать экспонометр, используя Arduino UNO, SSD1306 SPI OLED-дисплей, сенсорный модуль BH1750.

Если вы снимаете фотографии с помощью пленочных камер, вы можете использовать это руководство для создания собственного экспонометра, который можно носить с собой. Измеритель также может пригодиться для портретной фотографии, так как вам всегда нужен хороший свет для наилучшего захвата объекта.

Типичный экспонометр

Результаты датчика BH1750 могут быть неточными на 100%, но оно того стоит. В аналоговой фотографии люди все время использовали экспонометры для измерения света, соответствующего ISO их пленки.

В настоящее время для цифровых зеркальных камер всё цифровое и встроенное. Экспонометр — это в основном инструмент для измерения количества света, падающего на объект или отражающегося от объекта. Устройство собирает данные и преобразует результаты измерений в полезную информацию, которая затем сообщает скорость затвора и размер диафрагмы, необходимые для получения хороших фотографий.