Library for BH1750 I2C light sensor. Supports autoranging! True to datasheet. (beware: calls are blocking at the moment)

So there is this light sensor (like the one present in any phone), BH1750. I got mine soldered to some nice breakout board.

Few facts about this library: it is very true to the datasheet. it takes advantage of MTreg (time of measurement), which lets you to increase resolution up to 3.86 times at the cost of time it takes to perform a measurement. Measurement is blocking ! I've also implemented an autoranging feature. Just like in those fancy multimeters! It works as follows, thresholds are hard-coded:

  1. <5 lx switches to Hi-res2 and to max MTreg (so measurement time is like 500ms)
  2. <1000 lx Hi-res, MTreg default.
  3. above there is no real need for Hi-res, so Low-res mode is used.

Last note: I greatly recommend you to use log10 value of the luminosity data. it makes much more sense, because it becomes linear.

BH1750.cpp

Committer:
amateusz
Date:
2017-08-22
Revision:
0:a7280a6c3c9b

File content as of revision 0:a7280a6c3c9b:

#include "BH1750.h"


BH1750::BH1750(I2C & i2c_inst, bool autoRange, bool addressPinState): _i2c_inst(i2c_inst) {
    if (addressPinState == true)   _address = ADDRESS_HIGH;
    else _address = ADDRESS_LOW;

    _autoModeAdjustSwitch = autoRange;
}


void BH1750::_sendCommand(char toSend) {
    char toSendPseudoArray[] = {toSend};
    _i2c_inst.write(_address << 1, toSendPseudoArray, 1);
}

unsigned int BH1750::_readRaw() {
    unsigned int measurement;
    char measurement_intermediate [2];
    _i2c_inst.read(_address << 1, measurement_intermediate, 2);
    measurement = (measurement_intermediate[0] << 8) +  measurement_intermediate[1];
    return measurement;
}
bool BH1750::_autoModeAdjust(float measurement) { // returns if adjusted or left alone
//        printf("\tcurrentMode: %d\r\n", _currentMode);
    char previousMode = _currentMode;
    char previousMtreg = _currentMtreg;
    bool changed = false;

    if (measurement < 5.0 ) {
        if (_currentMode != CONTINOUS_H2_RES_CMD ) {
            setMode(CONTINOUS_H2_RES_CMD);
            setMtreg(254); //maximum
            changed = true;
        }
    } else if (measurement < 1000.0) {
        if (_currentMode != CONTINOUS_H_RES_CMD ) {

            setMtreg(DEFAULTMTREG);
            setMode(CONTINOUS_H_RES_CMD);
            changed = true;
        }
    } else if (_currentMode != CONTINOUS_L_RES_CMD) {
        setMtreg(DEFAULTMTREG);
        setMode(CONTINOUS_L_RES_CMD);
        changed = true;
    }
    
    if (changed) {
        wait_ms(_waitForMeasurement); // one more wait in PREVIOUS state. guarantees valid values on state transitions
        return true;
    } else
        return false;
}

float BH1750::_readSingle() {
    float measurement;
    // measurement switch
    switch(_currentMode) {
        case ONETIME_L_RES_CMD:
            _sendCommand(_currentMode);
            _waitForMeasurement = L_RES_MEASUREMENT_TIME;
            break;
        case ONETIME_H_RES_CMD:
            _sendCommand(_currentMode);
            _waitForMeasurement = H_RES_MEASUREMENT_TIME;
            break;
        case ONETIME_H2_RES_CMD:
            _sendCommand(_currentMode);
            _waitForMeasurement = H_RES_MEASUREMENT_TIME;
            break;
        case CONTINOUS_L_RES_CMD:
            _waitForMeasurement = L_RES_MEASUREMENT_TIME;
            break;
        case CONTINOUS_H_RES_CMD:
            _waitForMeasurement = H_RES_MEASUREMENT_TIME;
            break;
        case CONTINOUS_H2_RES_CMD:
            _waitForMeasurement = H_RES_MEASUREMENT_TIME;
            break;
    }

    _waitForMeasurement *= (unsigned int) ((float) _currentMtreg / (float) DEFAULTMTREG); // a bit too late, but nevermind
    wait_ms(_waitForMeasurement);
    measurement = (float)_readRaw();

    // post-measurement switch
    switch(_currentMode) {
        case ONETIME_L_RES_CMD:
        case ONETIME_H_RES_CMD:
        case ONETIME_H2_RES_CMD:
            break;

        case CONTINOUS_L_RES_CMD:
            break;
        case CONTINOUS_H_RES_CMD:
            break;
        case CONTINOUS_H2_RES_CMD:
            measurement /= 2.0;
            break;
    }

//        printf("wait for meas value: %d\r\n", _waitForMeasurement);

    measurement = measurement / 1.2 * (float) DEFAULTMTREG / (float) _currentMtreg;
    return measurement;
}

void BH1750::power(bool state) {
    if (state == true) {// power on
        _sendCommand(powerOn_cmd);
    } else { // power off}
        _sendCommand(powerDown_cmd);
    }
}

float BH1750::read() {
        float measurement;
        do {
            measurement = _readSingle();
//            printf("measurement do while: %.1f\r\n", measurement);
            if (!_autoModeAdjustSwitch) break;
            // else continue
//            wait_ms(1.3*_waitForMeasurement);
        } while(_autoModeAdjust(measurement));
        return measurement;
}

void BH1750::setMode(const char mode) {
    _sendCommand(mode);
    _currentMode = mode;
    wait(.01);
}

void BH1750::setMtreg(char newMtreg) {
    // min. 31
    // default 69
    // max. 254
    if (newMtreg >= 31 && newMtreg <= 254) {
        _sendCommand((0b01000 << 3) | (newMtreg >> 5));
        _sendCommand((0b011 << 5 )  | (newMtreg & 0b111));
        _currentMtreg = newMtreg;
    }
}