El BH1750 es un sensor de iluminación ambiental con una resolución y sensibilidad razonablemente altas. Se comporta frente a la luz visible de una forma equiparable a la del ojo humano y no se ve afectado por la radiación infrarroja ni depende de la temperatura de color del tipo de iluminación, es decir, funciona bien con luz natural y con diferentes tipos de iluminación artificial. Se comunica de forma digital con el microcontrolador, con el bus I2C, por lo que es resistente a interferencias si se sitúa a cierta distancia del circuito que lo lee. Su tiempo de respuesta es bastante bajo, menos de 200 ms en las circunstancias más desfavorables.
Desde el punto de vista electrónico su implementación es muy sencilla. Sólo hay que conectar la alimentación (entre 2,4 V y 3,6 V) y el bus I2C. Opcionalmente puede cambiarse la dirección del bus para conectar dos dispositivos (con ADDR a nivel bajo es 0B0100011 o 0x23 y con ADDR a nivel alto es 0B1011100 o 0x5C) y utilizarse la línea VDI para la función reset con el microcontrolador.
Para conectar el BH1750 a Arduino, además de alimentarse con la salida de 3,3 V, lo más correcto es usar un conversor de nivel además de con las resistencias pull-up para el bus I2C. Aunque el componente soportará una conexión directa al bus bus I2C no es recomendable dimensionar un circuito sin considerar convertir el nivel.
Por su popularidad, que viene dada por ser muy barato en relación a su precisión, existen varios módulos, como el conocido GY-30, que puede verse en la fotografía del principio. Para conectarlos de manera más cómoda al prototipar con un microcontrolador, suelen incluir conversores de nivel para el bus I2C y reguladores de tensión para alimentarlos con una tensión más alta (hasta 5 V) en lugar de la salida de 3,3 V de Arduino.
El BH1750 tiene dos modos de lectura, continua e individual, que corresponden con dos estados, activo y de bajo consumo o reposo. Mientras que si se utiliza el modo de lectura continua el BH1750 sigue activo tras el muestreo, después de realizar una medición individual entra automáticamente en el modo de reposo y bajo consumo. La primera lectura del modo continuo tarda a lo sumo 180 ms y las sucesivas entre 16 ms y 120 ms dependiendo de la resolución.
El sensor es capaz de medir a intervalos (resolución) de 4 lux, 1 lux y 0,5 lux. El BH1750 recomienda en su hoja de datos utilizar la resolución de 1 lux, que permite distinguir iluminaciones por debajo de los 10 lux (que corresponde con la luz crepuscular) y que es más inmune al ruido que podría afectar a la medida.
Las resoluciones de 1 lux y 4 lux utilizan los 16 bits de datos para representar la parte entera por lo que se puede alcanzar una medida máxima de 65535 lux (día soleado sin luz directa). El modo de 0,5 lux usa el bit menos significativo para la parte decimal (mide de 0,5 lux en 0,5 lux) por lo que con los 15 bits restante se puede conseguir representar un valor máximo de 32767 lux (exterior sin luz directa)
Normalmente la ventana óptica conforme a la cual se mide la luz ambiental corresponde a todo el espectro visible y se trata de conseguir una distribución de sensibilidad en el mismo equiparable a la humana. Si la ventana óptica se reduce (se mide luz en un rango de longitud de onda menor) se puede aumentar la sensibilidad del BH1750 (hasta 0,11 lux) con un modo de cancelación del ajuste de la influencia de la ventana óptica aumentando el tiempo de lectura en proporción. Como en este modo especial (sobredimensionado) se realizan lecturas separadas, el contexto debe permitirlo sin que se alteren especialmente las condiciones de medida (por ejemplo, el sensor debe permanecer muy estable, no debe moverse hacia una zona con diferentes condiciones de iluminación)
Códigos de operación del BH1750
Estado
5>
-
0B00000000
(0x00
) Modo de bajo consumo o inactividad.
-
0B00000001
(0x01
) Encendido.
-
0B00000111
(0x07
) Reset. Borra los registros de datos del BH1750.
Resolución
5>
-
0B00010011
(0x13
) Medida continua a 4 lux de resolución (entre 16 ms y de tiempo de lectura)
-
0B00010000
(0x10
) Medida continua a 1 lux de resolución (120 ms de tiempo de lectura)
-
0B00010001
(0x11
) Medida continua a 0,5 lux de resolución (120 ms de tiempo de lectura)
-
0B00100011
(0x23
) Una medición a 4 lux de resolución (16 ms de tiempo de lectura)
-
0B00100000
(0x20
) Una medición a 1 lux de resolución (120 ms de tiempo de lectura)
-
0B00100001
(0x21
) Una medición a 0,5 lux de resolución (120 ms de tiempo de lectura)
Ajuste por cambio en la ventana óptica
5>
-
0B011MT
[0,1,2,3,4] Bit bajo del registro MTREG (Measurement Time REGister).
-
0B01000MT
[5,6,7] Bit alto del registro MTREG.
Leer el BH1750 desde Arduino
0B00000000
(0x00
) Modo de bajo consumo o inactividad.
0B00000001
(0x01
) Encendido.
0B00000111
(0x07
) Reset. Borra los registros de datos del BH1750.
5>
-
0B00010011
(0x13
) Medida continua a 4 lux de resolución (entre 16 ms y de tiempo de lectura) -
0B00010000
(0x10
) Medida continua a 1 lux de resolución (120 ms de tiempo de lectura) -
0B00010001
(0x11
) Medida continua a 0,5 lux de resolución (120 ms de tiempo de lectura) -
0B00100011
(0x23
) Una medición a 4 lux de resolución (16 ms de tiempo de lectura) -
0B00100000
(0x20
) Una medición a 1 lux de resolución (120 ms de tiempo de lectura) -
0B00100001
(0x21
) Una medición a 0,5 lux de resolución (120 ms de tiempo de lectura)
Ajuste por cambio en la ventana óptica
5>
-
0B011MT
[0,1,2,3,4] Bit bajo del registro MTREG (Measurement Time REGister).
-
0B01000MT
[5,6,7] Bit alto del registro MTREG.
Leer el BH1750 desde Arduino
0B011MT
[0,1,2,3,4] Bit bajo del registro MTREG (Measurement Time REGister).
0B01000MT
[5,6,7] Bit alto del registro MTREG.
Para medir la iluminación ambiental con el BH1750 desde Arduino se utiliza la librería Wire que gestiona las comunicaciones con el bus I2C. El proceso es el habitual en este tipo de comunicaciones, en primer lugar se activan (una vez en el programa) con Wire.begin()
, se inicia la comunicación con el BH1750 con Wire.beginTransmission()
y su dirección I2C (0x5C o 0x23 según ADDR esté a nivel alto o bajo respectivamente), se configura enviando el código correspondiente con Wire.write()
y se libera el bus con Wire.endTransmission()
15 |
Wire.begin(); |
17 18 19 |
Wire.beginTransmission(DIRECCION_BH1750_0); Wire.write(MEDIDA_CONTINUA_UN_LUX_BH1750); Wire.endTransmission(); |
Si se utiliza uno de los modos de lectura continua, para obtener los datos se utiliza Wire.beginTransmission() con la dirección I2C correspondiente para acceder al BH1750, se le solicitan dos bytes (la resolución es de 16 bits) con Wire.requestFrom()
que se leen, usando Wire.read()
, y se cargan en un entero sin signo, rotando 8 bits el primer byte. Posteriormente se libera el bus con Wire.endTransmission()
. El resultado final se obtiene dividiendo el valor devuelto por el factor de precisión (1,2 si no se cambia la ventana óptica)
28 29 |
Wire.beginTransmission(DIRECCION_BH1750_0); Wire.requestFrom(DIRECCION_BH1750_0,2); |
37 38 |
lectura_BH1750=Wire.read()<<8; // Leer el primer byte y rotarlo 8 bits lectura_BH1750|=Wire.read(); // Leer el segundo byte y «juntarlo» con el anterior on OR |
40 |
iluminacion=long(100.0*(float)lectura_BH1750/DIVISOR_PRECISION)/100; // Resultado corregido y sin decimales |
Si se utiliza el modo de lecturas individuales el BH1750 entra en modo de reposo, para volver al modo activo se puede enviar una configuración (el mismo modo de lectura o uno nuevo) o el código de encendido (0x01). Para forzar al BH1750 a entrar en modo de reposo se puede utilizar el código de apagado (0x00).
Es importante respetar el tiempo de lectura del sensor que depende de la resolución. Si la espera no es crítica, se puede unificar en un valor para todos los casos que puede ser un poco mayor del máximo esperado para asegurarse de que se completa la lectura.
Para hacer más cómoda la escritura de código para el BH1750 en Arduino, en el siguiente documento de cabecera se encuentran los códigos de operación más relevantes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#define REPOSAR_BH1750 0x00 // Modo de reposo o bajo consumo #define ENCENDER_BH1750 0x01 #define RESETEAR_BH1750 0x07 #define MEDIDA_CONTINUA_CUATRO_LUX_BH1750 0x13 #define MEDIDA_CONTINUA_UN_LUX_BH1750 0x10 #define MEDIDA_CONTINUA_MEDIO_LUX_BH1750 0x11 #define MEDIDA_SIMPLE_CUATRO_LUX_BH1750 0x23 #define MEDIDA_SIMPLE_UN_LUX_BH1750 0x20 #define MEDIDA_SIMPLE_MEDIO_LUX_BH1750 0x21 #define ESPERA_BH1750_0 250 // 250 milisegundos de espera de lectura del BH1750 (mayor que la máxima) #define TIMEOUT_I2C 10 // 10 milisegundos de espera antes de renunciar a leer el bus I2C #define DIVISOR_PRECISION 1.2 // valor por el que dividir la lectura para calcular la luminosidad 1.2 si no hay cambios en la ventana óptica #define DIRECCION_BH1750_0 0x23 #define DIRECCION_BH1750_1 0x5C |
El siguiente código de ejemplo muestra el modo de lectura más habitual en el sensor de luz I2C BH1750. La resolución es de 1 lux y el modo de lectura continua. En el ejemplo se muestra, usando la consola serie de Arduino, cada resultado que se obtiene a partir del valor medido.
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 |
#include "BH1750.h" // Cargar los códigos de operación del BH1750 #include <Wire.h> unsigned int lectura_BH1750; unsigned int iluminacion; //float iluminacion; // Mostrar el valor con decimales long cronometro_lecturas=0; long tiempo_transcurrido; long cronometro_timeout_i2c; void setup() { Serial.begin(9600); Wire.begin(); delay(ESPERA_BH1750_0); // Espera inicial para estabilizar el BH1750 Wire.beginTransmission(DIRECCION_BH1750_0); Wire.write(MEDIDA_CONTINUA_UN_LUX_BH1750); Wire.endTransmission(); } void loop() { tiempo_transcurrido=millis()-cronometro_lecturas; if(tiempo_transcurrido>ESPERA_BH1750_0) { cronometro_lecturas=millis(); Wire.beginTransmission(DIRECCION_BH1750_0); Wire.requestFrom(DIRECCION_BH1750_0,2); do { tiempo_transcurrido=millis()-cronometro_timeout_i2c; } while(Wire.available()<2&&tiempo_transcurrido<TIMEOUT_I2C); if(Wire.available()>1) { lectura_BH1750=Wire.read()<<8; // Leer el primer byte y rotarlo 8 bits lectura_BH1750|=Wire.read(); // Leer el segundo byte y «juntarlo» con el anterior on OR //iluminacion=lectura_BH1750/DIVISOR_PRECISION; // Resultado corregido y con decimales (mínimamente más preciso pero menos legible) iluminacion=long(100.0*(float)lectura_BH1750/DIVISOR_PRECISION)/100; // Resultado corregido y sin decimales Serial.print("La iluminación es de "); Serial.print(iluminacion); Serial.println(" lux"); } } } |
Como decía más arriba, tanto el modo de resolución de 1 lux como el de 4 lux utilizan los 16 bits de datos para expresar la medida como un entero. En cambio, el modo de 0,5 lux el último bit representa una parte decimal, es decir, el valor que aporta al total de la medida está desplazado una potencia de dos a la derecha. En el modo de 1 lux o de 4 lux el último bit (LSB) vale 20, el penúltimo 21, el siguiente 22… en el modo de 0,5 lux el último bit (LSB) vale 2-1, el penúltimo 20, el siguiente 21…
Conforme a esta estructura de datos, y considerando que hay que realizar dos lecturas I2C de un byte, para obtener el valor de 16 bits hay que cargar los bits del byte más significativos, los primeros que se leen, y rotarlos 8 bits a la izquierda en el modo de resolución de 1 lux y en el de 4 lux y solamente 7 bits en el de 0,5 lux. Para unificar la forma de lectura en el modo de 0,5 lux se puede cargar el byte más significativo en un entero sin signo, rotar 8 bits a la izquierda, cargar el byte menos significativo y rotar todo el entero sin signo 1 bit a la derecha, preservando el valor de la parte decimal que indica el LSB (bit menos significativo) para aplicarlo después.
Lógicamentem para los modos de 1 lux o de 4 lux es necesario utilizar enteros sin signo (unsigned int
) para que Arduino no reserve el MSB (bit más significativo) para el signo y poder operar directamente con el valor verdadero de la medida, no con un número negativo. En Arduino Due no es necesario ya que los enteros utilizan 32 bits, pero el mismo programa también funcionará si se utiliza también unsigned int
.
El siguiente código muestra cómo se utilizaría el modo de 0,5 lux
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 |
#include "BH1750.h" // Cargar los códigos de operación del BH1750 #include <Wire.h> unsigned int lectura_BH1750; //unsigned int iluminacion; float iluminacion; // Mostrar el valor con decimales long cronometro_lecturas=0; long tiempo_transcurrido; long cronometro_timeout_i2c; bool parte_decimal; void setup() { Serial.begin(9600); Wire.begin(); delay(ESPERA_BH1750_0); // Espera inicial para estabilizar el BH1750 Wire.beginTransmission(DIRECCION_BH1750_0); Wire.write(MEDIDA_CONTINUA_MEDIO_LUX_BH1750); Wire.endTransmission(); } void loop() { tiempo_transcurrido=millis()-cronometro_lecturas; if(tiempo_transcurrido>ESPERA_BH1750_0) { cronometro_lecturas=millis(); Wire.beginTransmission(DIRECCION_BH1750_0); Wire.requestFrom(DIRECCION_BH1750_0,2); do { tiempo_transcurrido=millis()-cronometro_timeout_i2c; } while(Wire.available()<2&&tiempo_transcurrido<TIMEOUT_I2C); if(Wire.available()>1) { lectura_BH1750=Wire.read()<<8; // Leer el primer byte y rotarlo 8 bits lectura_BH1750|=Wire.read(); // Leer el segundo byte y «juntarlo» con el anterior on OR parte_decimal=(lectura_BH1750<<15)>0; lectura_BH1750>>=1; iluminacion=((float)lectura_BH1750+(parte_decimal?0.5:0.0))/DIVISOR_PRECISION; Serial.print("La iluminación es de "); Serial.print(iluminacion); Serial.println(" lux"); } } } |
Jose
disculpe pero estoy tratando de hacer un luxometro implementando este tipo de sensor y mi arduino no reconoce la libreria BH1750 pe podia recomendar una solucion
Se lo agradeceria
Víctor Ventura
Hola, Jose.
Me encantaría poder ayudarte pero, con la información que das, no tengo ni la menor idea de cuál puede ser el problema ¿En qué notas que Arduino no «reconoce» la librería? ¿Has usado los documentos de ejemplo? ¿Es por integrarla en el IDE? ¿Muestra un error? ¿Interfiere con otra? ¿Es problema del I2C? 🙁
Saludos
kike
Hola Jose
Prueba a sustituir #include «BH1750.h» por #include . Las dobles comillas las usa C++. Ahhh, instala la libreria antes también en el arduino.
Libreria https://github.com/claws/BH1750
Suerte y ánimo.
Gran Página 🙂
kike
Me comí una la solución jejeej. Cambia
Esto….. #include «BH1750.h» // Cargar los códigos de operación del BH1750
Por esto #include // Cargar los códigos de operación del BH1750
Mart
Hola! Tendra experiencia en medir el flicker de una luz con el BH1750?
Christian Suarez
Hola que tal me gusto mucho su información y es muy clara, a mi me gustaría cambiar los pines análogos del arduino y conectarlo en a2 y a3(normalmente esta en a4 y a5) pero no encuentro una solución espero pueda ayudarme muchas gracias
Felipe
Hola…. como podria hacer un PID con el bus de comunicación IC2?…. estoy haciendo algo similar pero usando tsl2561 el cual usa el mismo protocolo, pero ademas quiero obtener el valor de medicion para incorporarlo a un PID y poder varias una salida en PWN..
Saludos.