BH1750 PROGRAMMING IN ARDUINO UNO

HI,
I am trying to measure light intensity with sensor BH1750 and arduino uno.
Following is the wiring connection I made
VCC-3.3v
GND-GND
SCL-analog pin 5
SDA-analog pin 4
ADD-No connection
and the code is as follows

#include <Wire.h> //BH1750 IIC Mode 
#include <math.h> 
int BH1750address = 0x23; //setting i2c address
 
byte buff[2];
void setup()
{
  Wire.begin();
  Serial.begin(57600);//init Serail band rate
}
 
void loop()
{
  int i;
  uint16_t val=0;
  BH1750_Init(BH1750address);
  delay(200);
  if(2==BH1750_Read(BH1750address))
  {
    val=((buff[0]<<8)|buff[1])/1.2;
    Serial.print(val,DEC);     
    Serial.println("[lx]"); 
  }
  delay(150);
}
 
int BH1750_Read(int address) //
{
  int i=0;
  Wire.beginTransmission(address);
  Wire.requestFrom(address, 2);
  while(Wire.available()) //
  {
    buff[i] = Wire.read();  // receive one byte
    i++;
  }
  Wire.endTransmission();  
  return i;
}
 
void BH1750_Init(int address) 
{
  Wire.beginTransmission(address);
  Wire.write(0x10);//1lx reolution 120ms
  Wire.endTransmission();
}

but i am not getting any lux values. I doubt that the execution doesn't go into the loop

 if(2==BH1750_Read(BH1750address))

I got this program from Light_Sensor__SKU_SEN0097_-DFRobot
Can someone Pls explain me the working of the program so that I can understand where it is going wrong?!

Run the i2c scanner, to see if the wiring is okay and the I2C-bus is working.
http://playground.arduino.cc/Main/I2cScanner

hi peter,

Yes.I2C bus is working.
I2C Device found at address 0x23

I think the example is wrong, but I have to read the datasheet to understand how to read from the chip.
There should be no beginTransmission and endTransmission around the requestFrom. It is a common mistake, and sometimes the code works (or worked in a previous version or Arduino).

Have a look at this example: Wire - Arduino Reference
That is how requestFrom should be used.

I could not find good example code for the BH1750, sorry.

Hi peter,

I have attached the data sheet here.

is this the change you are saying?

#include <Wire.h> //BH1750 IIC Mode 
#include <math.h> 
int BH1750address = 0x23; //setting i2c address
 
byte buff[2];
void setup()
{
  Wire.begin();
  Serial.begin(57600);//init Serail band rate
}
 
void loop()
{
  int i;
  uint16_t val=0;
  BH1750_Init(BH1750address);
  delay(200);
  Serial.println("[lx]"); 
  if(2==BH1750_Read(BH1750address))
  {
    val=((buff[0]<<8)|buff[1])/1.2;
    Serial.print(val,DEC);     
    Serial.println("[lx]"); 
  }
  delay(150);
}
 
int BH1750_Read(int address) //
{
  int i=0;
  
  Wire.requestFrom(address, 2);
  while(Wire.available()) //
  {
    buff[i] = Wire.read();  // receive one byte
    i++;
  }
  
  return i;
}
 
void BH1750_Init(int address) 
{
  Wire.beginTransmission(address);
  Wire.write(0x10);//1lx reolution 120ms
  Wire.endTransmission();
}

BH1750FVI.pdf (434 KB)

According to the datasheet, the 2 bytes result can be read after a little delay.
The requestFrom does just do that. So your code should be better now.

Could you print the values from within the BH1750_Read function ?

int BH1750_Read(int address) //
{
  int i=0;

  Serial.println(F("Read function: "));   // added
  
  Wire.requestFrom(address, 2);
  while(Wire.available()) //
  {
    buff[i] = Wire.read();  // receive one byte
 
    Serial.print(buff[i], DEC);   // added
    Serial.print(F(", "));   // added

    i++;
  }
  Serial.prinln();

  return i;
}

To write working code, I should have a BH1750 myself...

Here is the code

#include <Wire.h> //BH1750 IIC Mode 
#include <math.h> 
int BH1750address = 0x23; //setting i2c address
 
byte buff[2];
void setup()
{
  Wire.begin();
  Serial.begin(57600);//init Serail band rate
}
 
void loop()
{
  int i;
  uint16_t val=0;
  BH1750_Init(BH1750address);
  delay(200);
  Serial.println("[lx]"); 
  if(2==BH1750_Read(BH1750address))
  {
    val=((buff[0]<<8)|buff[1])/1.2;
    Serial.print(val,DEC);     
    Serial.println("[lx]"); 
  }
  delay(150);
}
 
int BH1750_Read(int address) //
{
  int i=0;

  Serial.println(F("Read function: "));   // added
  
  Wire.requestFrom(address, 2);
  while(Wire.available()) //
  {
    buff[i] = Wire.read();  // receive one byte
 
    Serial.print(buff[i], DEC);   // added
    Serial.print(F(", "));   // added

    i++;
  }
  Serial.println();

  return i;
}
void BH1750_Init(int address) 
{
  Wire.beginTransmission(address);
  Wire.write(0x10);//1lx reolution 120ms
  Wire.endTransmission();
}

I have attached the output image with this

Do those values make sense ? Is it working ?

Hi peter,
Sorry for the late reply. Those values doesn't match with actual lux value (~300 lux) which I measured with a light meter
Can you Pls explain the following part of the code to me?

uint16_t val=0;

Is uint16_t is a data type of the variable val?. If it is so,Why doesn't my compiler display it in red font which it generally does for data types and commands?

if(2==BH1750_Read(BH1750address))

Why does if statement compares the array variable buff size to 2? does BH1750 send only two byte of data per communication?

val=((buff[0]<<8)|buff[1])/1.2;

what is the basis of this calculation?.i can understand that symbol << left shifts buff[0] value by 8 bits and it performs bit wise OR operation with buff[1] and the factor 1.2 is for some accuracy as per data sheet to find illuminance per count. But I couldn't understand how does this operation results in final illuminance value in lux!

 Serial.print(F(", "));

as per this link Serial.print() - Arduino Reference
it says
You can pass flash-memory based strings to Serial.print() by wrapping them with F().
But what does flash memory based strings mean?

By knowing the code well,I will have a better idea of how it works and can debug easily. Your help will be greatly appreciated

Here is a short answer, just ask if you want to know more.

I read the datasheet, but I don't understand why you read 3000 with 300 lux.

The uint16_t is a notation for an unsigned int of 16-bits. The sensor returns 16-bits, so that is what should be used.
Using "unsigned int" could be 32-bit on other devices.
http://arduino.cc/en/Reference/UnsignedInt

The Wire.requestFrom asks for 2 bytes. If everything is okay, 2 bytes are received. So "BH1750_Read(BH1750address)" returns the number 2 if everything is okay.

val = ( ( buff [ 0 ] << 8 ) | buff [ 1 ] )
Two bytes are received via I2C, and they should form an unsigned integer.
So how to do that. Shifting the high byte into place and just dropping the lower byte into place is one way to do it.

http://playground.arduino.cc/Learning/Memory
The use of memory can cause endless discussions. Some users get very personal when it comes to memory use :roll_eyes:
The RAM is very precious because that is where variables are stored.
The flash memory contains the sketch code.
When a string is used, it uses RAM memory, but with the 'F()' function, it is only in flash memory, next to the sketch code.
Using F(", ") is not very useful, since that string is only 3 bytes :~

Thanks a lot for clarifying peter :slight_smile:

I still don't understand why I get 3000 Lux for 300 lux :frowning: any help on that front will be very useful :slight_smile:

Hi peter,
My I2C device is not found at the address. I ran the I2c check code and verified it. Have I spoiled the sensor? How to debug now?

Perhaps the sensor is damaged, but in most cases there is something wrong with the wiring.
If the i2c_scanner can't find the device, you can't do anything else.
Do you have a multimeter ? to measure the voltages ?

I would like to see a photo of your module (also called : breakout board). Is there a voltage regulator for 3.3V on that module ? If so, you can power it with 5V, which could make a difference for working or not-working I2C.

Or do you use a breadboard ? Those can have bad contacts, you can shift everything a few posisitions and check if that helps.

Thank you peter :).It works now.
I think the problem was because of loose connections

Hi peter,
I am getting correct lux values with the following program.

#include <Wire.h> //BH1750 IIC Mode 
#include <math.h> 
int BH1750address = 0x23; //setting i2c address
 
byte buff[2];
void setup()
{
  Wire.begin();
  Serial.begin(57600);//init Serail band rate
}
 
void loop()
{
  int i;
  uint16_t val=0;
  BH1750_Init(BH1750address);
  delay(200);

  if(2==BH1750_Read(BH1750address))
  {
    
      val=((buff[0]<<8)|buff[1])/1.2;
    val=val+15;
      Serial.println("VAL="); 
    Serial.print(val,DEC);     
    Serial.println("[lx]"); 
  }
  delay(150);
}
 
int BH1750_Read(int address) //
{
  int i=0;

  Serial.println(F("Read function: "));   // added
  
  Wire.requestFrom(address, 2);
  while(Wire.available()) //
  {
    buff[i] = Wire.read();  // receive one byte
 
    Serial.print(buff[i], DEC);   // added
    Serial.print(F(", "));   // added

    i++;
  }
  Serial.println();

  return i;
}
void BH1750_Init(int address) 
{
  Wire.beginTransmission(address);
  Wire.write(0x10);//1lx reolution 120ms
  Wire.endTransmission();
}

But when I try to combine lighting control and the sensing part together as in following program,i am not getting any output from the sensor.

#include <Wire.h> //BH1750 IIC Mode 
#include <math.h>  
const int analogOutPin = 11; // Analog output pin that the LED is attached to
int BH1750address = 0x23;
byte buff[2];
int outputValue = 0;        

void setup() {
  // initialize serial communications at 9600 bps:
  Serial.begin(57600); 
}

void loop() {
  // read the analog in value:
   int i;
  uint16_t sensorval=0;
  BH1750_Init(BH1750address);
  delay(200);
if(2==BH1750_Read(BH1750address))
  {
    sensorval=((buff[0]<<8)|buff[1])/1.2;
    sensorval=sensorval+15;
    Serial.print(sensorval,DEC);     
    Serial.println("[lx]"); 
  }
             
  // map it to the range of the analog out:
  outputValue = map(sensorval, 380, 0, 0, 255);  //mapped lux value to output pwm value as i want output lux to be constant at 380
  // change the analog out value:
   digitalWrite(9, HIGH); //goes to In1 pin of LM 298 driver
 digitalWrite(10, LOW); //goes to In2  pin of LM 298 driver
  analogWrite(analogOutPin, outputValue);    //       goes to ENA  pin of LM 298 driver

  // print the results to the serial monitor:
   
  Serial.print("\t output = ");      
  Serial.println(outputValue);   

  // wait 2 milliseconds before the next loop
  // for the analog-to-digital converter to settle
  // after the last reading:
  delay(2);                     
}
int BH1750_Read(int address) //
{
  int i=0;

  Serial.println(F("Read function: "));   // added
  
  Wire.requestFrom(address, 2);
  while(Wire.available()) //
  {
    buff[i] = Wire.read();  // receive one byte
 
    Serial.print(buff[i], DEC);   // added
    Serial.print(F(", "));   // added

    i++;
  }
  Serial.println();

  return i;
}
void BH1750_Init(int address) 
{
  Wire.beginTransmission(address);
  Wire.write(0x10);//1lx reolution 120ms
  Wire.endTransmission();
}

I doubt the program control doesn't go inside the following loop

  while(Wire.available()) //
  {
    buff[i] = Wire.read();  // receive one byte
 
    Serial.print(buff[i], DEC);   // added
    Serial.print(F(", "));   // added

    i++;
  }

I have attached my output with this post.you could see in that output stops after certain iterations.I don't understand what is happening.can you please help me out

An output of 255 could indicate that the I2C bus is not working.
When it is working, a Wire.requestFrom(address, 2) can be followed by Wire.available(), followed by two Wire.read(). Just as you have. That should be okay.

I assumed that the input of the map() function should be lower first, but I think it has changed.
It says that : Note that the "lower bounds" of either range may be larger or smaller than the "upper bounds"
So that should not be a problem for you.

The input of the map() function could be wrong, because the calculation is a mix of everything.

This line of code : sensorval=((buff[0]<<8)|buff[1])/1.2;
Is like this : uint16_t = ( (byte << 8) | byte ) / float
When you divide by 1.2, you have to make everything float.

Did you use a resistor with that led ?

I checked with I2C communication verificationcode.and it is working fine.
I believe as it doesn't go to

while wire.available()

it doesn't return value '2'. so the following loop is also not executed

if(2==BH1750_Read(BH1750address))
  {
    sensorval=((buff[0]<<8)|buff[1])/1.2;
    sensorval=sensorval+15;
    Serial.print(sensorval,DEC);     
    Serial.println("[lx]"); 
  }

and since initially I have set the sensorval to be zero,it maps that value to 255. I tried changing initial sensorval from 0 to 100 and output value changed to 187. So mapping function is working fine,but the issue is that wire.available() is not executing.
Yes I use a 8.2 ohm resistor in series with the LED lamp

When you do Wire.requestFrom(address,2), you request 2 data bytes. After that the Wire.available() should return 2, or something is wrong.
Instead of the while-loop, you can just check if Wire.available() returns the value 2.

I hope you use a series resistor of 150 to 1000 ohm, not 8.2 ohm !

Hi,
I am not using BH1750 for now.Reference lux value is set to 350 and I use a LDR to sense the light and calibrated it into lux value in the program.
But I observe flickering in the light output because the output doesn't settle at desired 350 Lux.It keeps on oscillating and I dont achieve the desired closed loop.The sensor is kept right below the LED lamp
Following is the code

#include<math.h>
const int analogInPin = A0;  // Analog input pin that the potentiometer is attached to
const int analogOutPin = 11; // Analog output pin that the LED is attached to

int sensorValue = 0;        // value read from the pot
int outputValue = 0;        // value output to the PWM (analog out)

void setup() {
  // initialize serial communications at 9600 bps:
  Serial.begin(9600); 
}

void loop() {
  // read the analog in value:
  sensorValue = analogRead(analogInPin); 
// calibrate sensor value to lux value
float three=(-.000029333)*pow(sensorValue,3)  ;
float two=(.0311)*pow(sensorValue,2);
float one=(-11.1963)*sensorValue;
float lux= three+two+one+1426;
//limit lux value in the range 0-350 so that PWM value remains within 0-255
if(lux<=0)
{
  lux=0;
}
if(lux>=350)
{
  lux=350;
}
 Serial.println("lux= " );                       
  Serial.println(lux);    
 // map it to the range of the analog out:
  outputValue = map(lux, 350, 0, 0, 204);  
  // change the analog out value:
   digitalWrite(9, HIGH);
 digitalWrite(10, LOW);
  analogWrite(analogOutPin, outputValue);           

  // print the results to the serial monitor:
  Serial.println("sensor = " );                       
  Serial.print(sensorValue);      
  Serial.print("\t output = ");      
  Serial.println(outputValue);   

  // wait 2 milliseconds before the next loop
  // for the analog-to-digital converter to settle
  // after the last reading:
  delay(2);                     
}

I have also attached the output image here