BH1750FVI (GY-30, 302) Lichtsensormodul

Über den Beitrag

In diesem Beitrag meiner Reihe über Licht-, Gestik-, Bewegungs- und Abstandssensoren möchte ich den Lichtsensor BH1750FVI vorstellen. Genauer gesagt geht es um das BH1750FVI Modul – der BH1750FVI selbst ist der kleine sechsbeinige IC auf dem Modul. Oftmals findet man dazu auch die Kurzbezeichnung BH1750 oder auch nur GY-302 oder GY-30, dabei handelt es sich aber immer um das gleiche Modul, bzw. dieselbe Basis. 

Ein paar Beiträge zuvor habe ich den Lichtsensor TSL2561 vorgestellt, der auf den ersten Blick ähnlich wie der BH1750FVI arbeitet, aber im Detail doch deutliche Unterschied aufweist. Eine Gegenüberstellung dieser und aller anderen Sensoren dieser Reihe gibt es, wenn ich die Reihe abgeschlossen habe.  

In diesem Beitrag werde zunächst auf die technischen Eigenschaften eingehen, die Einstellmöglichkeiten vorstellen, die Ansteuerung per Arduino erklären und meine Bibliothek dazu vorstellen. Es gibt zwar schon eine ganze Reihe von Bibliotheken für den BH1750FVI, ich habe aber keine gefunden, bei der die Einstellung der Messzeit implementiert war und auch vollständig funktionierte. 

Eigenschaften

Verschiedene BH1750FVI Module
Zwei BH1750FVI Module

Funktionsprinzip

Der BH1750FVI besitzt eine Photodiode mit einem Ansprechbereich von ca. 400 – 700 nm, also im Wesentlichen dem Bereich des sichtbaren Lichts. Die Photospannung wird verstärkt und mit einem A/D-Wandler in einem 16-Bit Messwert gespeichert. Der Messwert wird dazu über einen einstellbaren Messzeitraum integriert. 

Technische Daten des BH1750FVI Moduls

Die wichtigsten technischen Daten sind:

  • Stromversorgung: 3 – 5 VDC
  • Stromaufnahme:  ~ 0.14 mA 
    • Power Off Modus: 0 mA  
  • Kommunikation: I2C (SDA/SCL)
  • I2C Adressen: 0x23 oder 0x5C einstellbar
  • Messbereich: 0 – 65535 lx, erweiterbar bis 100000 (lt. Datenblatt)
    • aus meiner Sicht stimmen die Werte nicht ganz – dazu später mehr 
  •  maximale Auflösung: einstellungsabhängig 0.11 – 7.4 lx

Die Standard I2C Adresse des Moduls ist 0x23. Ihr könnt sie durch Anschluss des Adresspins (ADDR oder ADD) an HIGH auf 0x5C ändern. Der Pin ist anscheinend „pulled down“. Deshalb ist für die Verwendung der Standardadresse ist kein Anschluss an GND notwendig. Ihr könnt die Adresse mit einem I2C Scanner überprüfen, zum Beispiel dem hier

Das Modul bekommt für ca. 2 Euro das Stück z.B. auf Amazon bei zahlreichen Anbietern. 

Ein ausführliches Datenblatt bekommt ihr hier.

Einstellmöglichkeiten / Betriebsmodi

Resolution Modes

Der BH1750HVI verfügt über drei kontinuierliche („Continously“) und drei diskontinuierliche („One Time“) Modi:

  • Continously H-Resolution Mode
  • Continously H-Resolution Mode2
  • Continously L-Resolution Mode
  • One Time H-Resolution Mode
  • One Time H-Resolution Mode2
  • One Time L-Resolution Mode

Der Vorteil der „One Time“ Modi ist, dass das Modul nach der Messung automatisch in den Power Off Modus geht und so Strom spart. 

Ansonsten unterscheiden sich die Modi hinsichtlich ihrer Auflösung und der Messzeit: 

Betriebsmodi des BH1750HVI vs Messzeit und Auflösung
Betriebsmodi des BH1750HVI versus Messzeit und Auflösung

Der Continuously H-Resolution Mode ist die Voreinstellung. In den L-Resolution Mode geht ihr, wenn ihr schnell bzw. in hoher Frequenz messen wollt. Der H-Resolution Mode2 bietet sich an, um eine höhere Auflösung im Dunkeln zu erreichen. 

Auflösung vs maximale Lichtstärke

Der Messwert wird als 16 Bit Wert gespeichert, entsprechend ist 65535 der Maximalwert. Zur Umrechnung in Lux müssen laut Datenblatt die Rohdaten im H-Resolution und L-Resolution Mode durch 1.2 geteilt werden. Daraus ergibt sich dann ein Maximalwert von 54612 lx. Im H-Resolution Mode2 muss das Ergebnis noch einmal durch zwei geteilt werden. Daraus ergibt sich ein Maximalwert von 27306 lx. 

Wie stellt man die Modi ein?

Wenn ihr keine Bibliothek verwendet, dann müsst Ihr zur Einstellung des gewünschten Modus lediglich ein einzelnes Steuerbyte (im Datenblatt OpeCode genannt) über I2C an den BH1750HVI senden. Später seht ihr in den Beispielsketchen wie das im Detail funktioniert.    

OpeCodes des BH1750FVI zur Einstellung der Auflösung
Einstellung der Auflösung mittels Steuercodes

Einstellung der Messzeit

Offen gestanden musste ich den Abschnitt zur Einstellungsprozedur der Messzeit mehrfach lesen, denn sie ist, sagen wir einmal ungewöhnlich. Zunächst einmal sollte man wissen, dass die Einstellung der Messzeit relativ zu der durch den Modus eingestellten Grundmesszeit, also 16 bzw. 120 ms, erfolgt. Das heißt, man gibt keinen Wert in Millisekunden an, sondern arbeitet mit Faktoren. 

Die Messzeit bzw. der Faktor wird im Measurement Time Register (MTReg) eingestellt.  Als Grundeinstellung steht dort der Wert 69 (0b01000101). Diesen Wert kann man von 31 bis 254 variieren. Eine Veränderung auf 31 bedeutet:

Neue Messzeit = Grundwert x 31/69 ≈ Grundwert x 0,45

Eine Veränderung auf den Maximalwert 254 bedeutet:

Neue Messzeit = Grundwert x 254/69 ≈ Grundwert x 3,68

Die Messzeit kann um den Faktor 0,45 bis 3,68 verändert werden. 

Die Messzeitveränderung schlägt sich direkt proportional auf die Rohdaten nieder. Entsprechend ändern sich auch die maximal messbaren Lichtstärken. Ein Beispiel: im Continuous H-Mode beträgt der maximal messbare Lux-Wert 65535 / 1.2 ≈ 54612. Senkt man die Messzeit auf das 0.45 fache, dann kann das Licht entsprechend intensiver sein, ohne dass das 16 Bit Datenregister überläuft. Der maximal messbare Lux-Wert liegt damit bei 65535 ⋅ (69/31) / 1.2 ≈ 121557 lx. Warum das Datenblatt 100000 lx als Maximalwert angibt, weiß ich nicht.

Wichtig ist, dass man die Faktoren bei der Umrechnung der Rohdaten in Lux Werte berücksichtigt, denn sonst sind sie natürlich falsch. Es kann aber auch einen Grund geben, genau das nicht zu tun, nämlich bei einer Kalibrierung. Dazu gleich mehr.     

Warum die Messzeit verändern?

Es gibt drei Gründe die Messzeit zu verändern:

  1. Den Messbereich nach oben erweitern
  2. Im unteren Mesbereich für eine höhere Auflösung sorgen 
  3. Kalibrierung, z.B. wegen Verwendung eines Fensters, das einen Teil des Lichtes absorbiert

Sagen wir einmal, der BH1750FVI würde hinter einem Fenster verbaut, welches nur 50% des Lichts durchlässt. In diesem Fall würde man die Messzeit verdoppeln um den Effekt auszugleichen (Kalbration). Und in diesem Fall würde man den Faktor natürlich nicht wie bei 1 und 2 am Ende wieder herausrechnen. 

Auflösung versus Messzeit

Aus den oben angegebenen Berechnungsvorschriften lässt sich die Auflösung in Abhängigkeit des MTReg Wertes ermitteln:

Auflösung in H-Resolution Mode = 1/1.2 x 69/MTReg

Auflösung in H-Resolution Mode2 = (1/1.2 x 69/MTReg)/2 

Auflösung in L-Resolution Mode = (1/1.2 x 69/MTReg) x 4

Daraus ergibt sich:

Auflösung versus Messzeit und Betriebsmodus
Auflösung versus Messzeit und Betriebsmodus

Wie verändert man die Messzeit?

Im Prinzip habe ich die Antwort auf diese Frage ja schon gegeben. Ihr müsst den Defaultwert im MTReg von 69 auf den gewünschten Wert ändern. Habt ihr eine Bibliothek, die diese Funktion implementiert hat, dann müsst ihr euch um die Details nicht kümmern und könnt den Rest des Abschnitts überspringen. 

Für diejenigen, die ohne Bibliothek arbeiten oder etwas in ihrer Bibliothek ändern wollen oder einfach nur wissen wollen, was hinter den Kulissen passiert, führe ich das weiter aus. 

Normalerweise ist man es gewohnt einen Registerwert zu verändern, indem man ein Kontrollregister anspricht, diesem das Zielregister mitteilt und dann byteweise die Werte übergibt. Hier läuft es ein bisschen anders. Die drei oberen Bits und die fünf unteren Bits des MTReg Wertes (kurz: MTReg) werden in zwei Schritten, verpackt in zwei Steuerbytes an den BH1750FVI übergeben. Das passiert nach folgendem Schema:

  • Byte1 = 01000 [MTReg Bit7] [MTReg Bit6] [MTReg Bit5]
  • Byte2 = 011 [MTReg Bit4] [MTReg Bit3] [MTReg Bit2] [MTReg Bit1] [MTReg Bit0]

Die Bytes berechnet man am besten über Binäroperationen:

Byte1 = (MTReg >> 5) | 0b01000000

Die Verschiebung um 5 Stellen positioniert die Bits 7,6 und 5 an die richtige Stelle. Die anschließende logische ODER Operation sorgt dafür, dass die unteren Bits unverändert bleiben und oben die 01000 steht.

Byte2 = (MTReg & 0b01111111) | 0b01100000

Hier sorgt das erste logische UND für eine 0 an Bit 7 des Byte2 ohne die anderen Stellen zu ändern. Die anschließende ODER Operation setzt Bit 6 und 7 ohne die anderen Bits zu verändern. 

Andere Einstellungen

Hier noch ein paar weitere Einstellungen mit den entsprechenden Steuercodes:

Weitere Einstellungen am BH1750FVI

Ansteuerung des BH1750FVI ohne Bibliothek

Der Sketch

Hier nun der Arduino Sketch für die Ansteuerung des BH1750FVI ohne Bibliothek.

Der MTReg Wert (measuringTimeFactor) wird als Faktor eingegeben und daraus der Byte Wert für das MT Register berechnet. Ich finde das praxisnäher als den MTReg Wert direkt festzulegen. In Zeile 38 bzw. 41 wird der Faktor wieder herausgerechnet um die „echten“ Lux Werte zu erhalten. Wird der Faktor zur Kalibrierung benutzt, dann ist dieser Rechenteilschritt zu unterlassen. 

Aufgrund der vorhergehenden Erklärungen sollte der Sketch ansonsten selbsterklärend sein. 

#include <Wire.h>
#define BH_1750 0x23
#define DATA_REG_RESET 0b00000111
#define POWER_DOWN 0b00000000
#define POWER_ON 0b00000001

enum BH1750Mode {
  CHM = 0b00010000,     //CHM: Continuously H-Resolution Mode
  CHM_2 = 0b00010001,   //CHM_2: Continuously H-Resolution Mode2
  CLM = 0b00010011,     //CLM: Continuously L-Resolution Mode
  OTH = 0b00100000,     //OTH: One Time H-Resolution Mode
  OTH_2 = 0b00100001,   //OTH_2: One Time H-Resolution Mode2
  OTL = 0b00100011      //OTL: One Time L-Resolution Mode
} mode;

float measuringTimeFactor;

void setup(){
  Serial.begin(9600);
  Wire.begin();
  mode = CHM;
  measuringTimeFactor = 1;
  setMode();
  setMeasuringTime(); 
  delay(200);
}

void loop(){ 
  getLux();
  delay(1000);
}

uint16_t getLux(){
  uint16_t rawLux;
  float lux;
  rawLux = readBH1750();
  if((mode==CHM_2)||(mode==OTH_2)){
    lux = (rawLux/2.4)/measuringTimeFactor;     
  }
  else{
    lux = (rawLux/1.2)/measuringTimeFactor;
  }
  Serial.print(F("Lichtstärke: "));
  Serial.print(lux);
  Serial.println(F(" Lux"));
}

void powerDown(){
  writeBH1750(POWER_DOWN);
}

void powerOn(){
  writeBH1750(POWER_ON);
  setMode();
}

void dataRegReset(){
  writeBH1750(DATA_REG_RESET);
}

void setMode(){
  writeBH1750(mode);
}

void setMeasuringTime(){
  byte mt = round(measuringTimeFactor*69);
  byte highByteMT = ((mt>>5) | 0b01000000);
  byte lowByteMT = (mt & 0b01111111);
  lowByteMT |= 0b01100000;
  writeBH1750(highByteMT);
  writeBH1750(lowByteMT);
}

void readBH1750(){
  uint8_t MSbyte, LSbyte;
  Wire.requestFrom(BH_1750, 2);
  if(Wire.available()){
    MSbyte=Wire.read();
    LSbyte=Wire.read(); 
  }
  return ((MSbyte<<8) + LSbyte);
}

void writeBH1750(byte val){
  Wire.beginTransmission(BH_1750);
  Wire.write(val);
  Wire.endTransmission();
}

 

Die Schaltung

Der Vollständigkeit halber hier noch die Schaltung, auch wenn sie alles andere als komplex ist. Es sei einzig noch angemerkt, dass ich auf Pull-Up Widerstände für die SDA / SCL Leitungen verzichtet habe. Es funktioniert auch ohne. Falls Ihr Probleme bekommt, fügt sie hinzu. 

Schaltung: der BH1750FVI am Arduino
Der BH1750FVI am Arduino

BH1750FVI Bibliothek

Da ich wie schon erwähnt keine Bibliothek gefunden habe, bei der die Einstellung der Messzeit zu meiner Zufriedenheit implementiert war, habe ich eine eigene geschrieben. Sie heißt BH1750_WE und ihr findet sie hier auf Github. Wenn ihr damit arbeiten wollt, dann ladet sie als zip-Datei herunter und entpackt sie im Arduino/libraries Ordner. Den folgenden Beispielsketch findet ihr im examples Ordner.   

/***************************************************************************
* Example sketch for the BH1750_WE library
* 
* Mode selection / abbreviations:
* CHM:    Continuously H-Resolution Mode
* CHM_2:  Continuously H-Resolution Mode2
* CLM:    Continuously L-Resolution Mode
* OTH:    One Time H-Resolution Mode
* OTH_2:  One Time H-Resolution Mode2
* OTL:    One Time L-Resolution Mode
* 
* Measuring time factor:
* 1.0 ==> Measuring Time Register = 69
* 0.45 ==> Measuring Time Register = 31
* 3.68 ==> Mesuring Time Register = 254
* 
* Other implemented functions, not used in the example:
* resetDataReg() --> rests Data Register
* powerOn() --> Wake Up!
* powerDown() --> Sleep well, my BH1750
* 
* If you change the measuring time factor for calibration purpose, 
* then you need to devide the light intensity by the measuring time factor 
* 
* Further information can be found on:
* http://wolles-elektronikkiste.de/bh1750fvi-lichtsensormodul
* Sorry - all in German!
***************************************************************************/

#include <Wire.h>
#include <BH1750_WE.h>
#define BH1750_ADDRESS 0x23

BH1750_WE myBH1750(BH1750_ADDRESS); 

void setup(){
  Serial.begin(9600);
  Wire.begin();
  myBH1750.init(); // sets default values: mode = CHM, measuring time factor = 1.0
  // myBH1750.setMode(CLM);  // uncomment if you want to change default values
  // myBH1750.setMeasuringTimeFactor(0.45); // uncomment for selection of value between 0.45 and 3.68
}

void loop(){ 
  float lightIntensity = myBH1750.getLux();
  Serial.print(F("Lichtstärke: "));
  Serial.print(lightIntensity);
  Serial.println(F(" Lux"));
  delay(1000);
}

 

Danksagungen

Den Hintergrund für das Beitragsbild stammt von Hanna Kovalchuk auf Pixabay.

Den BH1750FVI als Fritzing Bauteil habe ich von vdemay auf Github.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.