Luminosity sensor by Texas Advanced Optoelectronic Solutions Inc.. Device combines one broadband photodiode (visible plus infrared) and one infrared-responding photodiode. Sets Gain x1 and 402mS as default.

Dependents:   MusicBoxForFathersDay FTHR_SensorHub Affich_Lum_Moist Projetv0 ... more

TSL2561.cpp

Committer:
kenjiArai
Date:
2018-09-21
Revision:
5:5b1b625fda6f
Parent:
4:05d322353720

File content as of revision 5:5b1b625fda6f:

/*!
 * @file Adafruit_TSL2561_U.cpp
 *
 * @mainpage Adafruit TSL2561 Light/Lux sensor driver
 *
 * @section intro_sec Introduction
 *
 * This is the documentation for Adafruit's TSL2561 driver for the
 * Arduino platform.  It is designed specifically to work with the
 * Adafruit TSL2561 breakout: http://www.adafruit.com/products/439
 *
 * These sensors use I2C to communicate, 2 pins (SCL+SDA) are required
 * to interface with the breakout.
 *
 * Adafruit invests time and resources providing this open source code,
 * please support Adafruit and open-source hardware by purchasing
 * products from Adafruit!
 *
 * @section dependencies Dependencies
 *
 * This library depends on
 *  <a href="https://github.com/adafruit/Adafruit_Sensor">
 * Adafruit_Sensor</a> being present on your system. Please make sure you have
 * installed the latest version before using this library.
 *
 * @section author Author
 *
 * Written by Kevin "KTOWN" Townsend for Adafruit Industries.
 *
 * @section license License
 *
 * BSD license, all text here must be included in any redistribution.
 *
 *   @section  HISTORY
 *
 *   v2.0 - Rewrote driver for Adafruit_Sensor and Auto-Gain support, and
 *          added lux clipping check (returns 0 lux on sensor saturation)
 *   v1.0 - First release (previously TSL2561)
*/
/**************************************************************************/

//------- Modified by ----------------------------------------------------------
//  Kenji Arai / JH1PJL
//  http://www.page.sannet.ne.jp/kenjia/index.html
//  http://mbed.org/users/kenjiArai/
//      Created: Feburary   21st, 2015
//      Revised: August     23rd, 2017
//      Revised: Feburary   20th, 2018   bug fix -> read_ID() & who_am_i()
//                                       Thanks PARK JAICHANG
//      Revised: March      31st, 2018   Added "Auto Range mode"
//                                       Use Adafruit library
//
// Original information     https://www.adafruit.com/product/439
// Original source files    https://github.com/adafruit/TSL2561-Arduino-Library
//                          https://github.com/adafruit/Adafruit_TSL2561
// Change for Mbed platform
//    modified -> all related files
//------------------------------------------------------------------------------

#include "TSL2561.h"

// T, FN and CL package values
#define TSL2561_LUX_K1T           (0x0040)  ///< 0.125 * 2^RATIO_SCALE
#define TSL2561_LUX_B1T           (0x01f2)  ///< 0.0304 * 2^LUX_SCALE
#define TSL2561_LUX_M1T           (0x01be)  ///< 0.0272 * 2^LUX_SCALE
#define TSL2561_LUX_K2T           (0x0080)  ///< 0.250 * 2^RATIO_SCALE
#define TSL2561_LUX_B2T           (0x0214)  ///< 0.0325 * 2^LUX_SCALE
#define TSL2561_LUX_M2T           (0x02d1)  ///< 0.0440 * 2^LUX_SCALE
#define TSL2561_LUX_K3T           (0x00c0)  ///< 0.375 * 2^RATIO_SCALE
#define TSL2561_LUX_B3T           (0x023f)  ///< 0.0351 * 2^LUX_SCALE
#define TSL2561_LUX_M3T           (0x037b)  ///< 0.0544 * 2^LUX_SCALE
#define TSL2561_LUX_K4T           (0x0100)  ///< 0.50 * 2^RATIO_SCALE
#define TSL2561_LUX_B4T           (0x0270)  ///< 0.0381 * 2^LUX_SCALE
#define TSL2561_LUX_M4T           (0x03fe)  ///< 0.0624 * 2^LUX_SCALE
#define TSL2561_LUX_K5T           (0x0138)  ///< 0.61 * 2^RATIO_SCALE
#define TSL2561_LUX_B5T           (0x016f)  ///< 0.0224 * 2^LUX_SCALE
#define TSL2561_LUX_M5T           (0x01fc)  ///< 0.0310 * 2^LUX_SCALE
#define TSL2561_LUX_K6T           (0x019a)  ///< 0.80 * 2^RATIO_SCALE
#define TSL2561_LUX_B6T           (0x00d2)  ///< 0.0128 * 2^LUX_SCALE
#define TSL2561_LUX_M6T           (0x00fb)  ///< 0.0153 * 2^LUX_SCALE
#define TSL2561_LUX_K7T           (0x029a)  ///< 1.3 * 2^RATIO_SCALE
#define TSL2561_LUX_B7T           (0x0018)  ///< 0.00146 * 2^LUX_SCALE
#define TSL2561_LUX_M7T           (0x0012)  ///< 0.00112 * 2^LUX_SCALE
#define TSL2561_LUX_K8T           (0x029a)  ///< 1.3 * 2^RATIO_SCALE
#define TSL2561_LUX_B8T           (0x0000)  ///< 0.000 * 2^LUX_SCALE
#define TSL2561_LUX_M8T           (0x0000)  ///< 0.000 * 2^LUX_SCALE

// CS package values
#define TSL2561_LUX_K1C           (0x0043)  ///< 0.130 * 2^RATIO_SCALE
#define TSL2561_LUX_B1C           (0x0204)  ///< 0.0315 * 2^LUX_SCALE
#define TSL2561_LUX_M1C           (0x01ad)  ///< 0.0262 * 2^LUX_SCALE
#define TSL2561_LUX_K2C           (0x0085)  ///< 0.260 * 2^RATIO_SCALE
#define TSL2561_LUX_B2C           (0x0228)  ///< 0.0337 * 2^LUX_SCALE
#define TSL2561_LUX_M2C           (0x02c1)  ///< 0.0430 * 2^LUX_SCALE
#define TSL2561_LUX_K3C           (0x00c8)  ///< 0.390 * 2^RATIO_SCALE
#define TSL2561_LUX_B3C           (0x0253)  ///< 0.0363 * 2^LUX_SCALE
#define TSL2561_LUX_M3C           (0x0363)  ///< 0.0529 * 2^LUX_SCALE
#define TSL2561_LUX_K4C           (0x010a)  ///< 0.520 * 2^RATIO_SCALE
#define TSL2561_LUX_B4C           (0x0282)  ///< 0.0392 * 2^LUX_SCALE
#define TSL2561_LUX_M4C           (0x03df)  ///< 0.0605 * 2^LUX_SCALE
#define TSL2561_LUX_K5C           (0x014d)  ///< 0.65 * 2^RATIO_SCALE
#define TSL2561_LUX_B5C           (0x0177)  ///< 0.0229 * 2^LUX_SCALE
#define TSL2561_LUX_M5C           (0x01dd)  ///< 0.0291 * 2^LUX_SCALE
#define TSL2561_LUX_K6C           (0x019a)  ///< 0.80 * 2^RATIO_SCALE
#define TSL2561_LUX_B6C           (0x0101)  ///< 0.0157 * 2^LUX_SCALE
#define TSL2561_LUX_M6C           (0x0127)  ///< 0.0180 * 2^LUX_SCALE
#define TSL2561_LUX_K7C           (0x029a)  ///< 1.3 * 2^RATIO_SCALE
#define TSL2561_LUX_B7C           (0x0037)  ///< 0.00338 * 2^LUX_SCALE
#define TSL2561_LUX_M7C           (0x002b)  ///< 0.00260 * 2^LUX_SCALE
#define TSL2561_LUX_K8C           (0x029a)  ///< 1.3 * 2^RATIO_SCALE
#define TSL2561_LUX_B8C           (0x0000)  ///< 0.000 * 2^LUX_SCALE
#define TSL2561_LUX_M8C           (0x0000)  ///< 0.000 * 2^LUX_SCALE

// Auto-gain thresholds
#define TSL2561_AGC_THI_13MS      (4850)    ///< Max value at Ti 13ms = 5047
#define TSL2561_AGC_TLO_13MS      (100)     ///< Min value at Ti 13ms = 100
#define TSL2561_AGC_THI_101MS     (36000)   ///< Max value at Ti 101ms = 37177
#define TSL2561_AGC_TLO_101MS     (200)     ///< Min value at Ti 101ms = 200
#define TSL2561_AGC_THI_402MS     (63000)   ///< Max value at Ti 402ms = 65535
#define TSL2561_AGC_TLO_402MS     (500)     ///< Min value at Ti 402ms = 500

// Clipping thresholds  ///< # Counts that trigger a change in gain/integration
#define TSL2561_CLIPPING_13MS     (4900)
#define TSL2561_CLIPPING_101MS    (37000)
#define TSL2561_CLIPPING_402MS    (65000)

// Delay for integration times
#define TSL2561_DELAY_INTTIME_13MS  (15)   ///< Wait 15ms for 13ms integration
#define TSL2561_DELAY_INTTIME_101MS (120)  ///< Wait 120ms for 101ms integration
#define TSL2561_DELAY_INTTIME_402MS (450)  ///< Wait 450ms for 402ms integration

/*========================================================================*/
/*                            CONSTRUCTORS                                */
/*========================================================================*/

TSL2561::TSL2561 (PinName p_sda, PinName p_scl)
    : _i2c_p(new I2C(p_sda, p_scl)), _i2c(*_i2c_p)
{
    _addr = TSL2561_ADDR_LOW;
    init();
}

TSL2561::TSL2561 (PinName p_sda, PinName p_scl, uint8_t addr)
    : _i2c_p(new I2C(p_sda, p_scl)), _i2c(*_i2c_p)
{
    _addr = addr;
    init();
}

TSL2561::TSL2561 (I2C& p_i2c)
    : _i2c(p_i2c)
{
    _addr = TSL2561_ADDR_LOW;
    init();
}

TSL2561::TSL2561 (I2C& p_i2c, uint8_t addr)
    : _i2c(p_i2c)
{
    _addr = addr;
    init();
}

/*========================================================================*/
/*                           PUBLIC FUNCTIONS                             */
/*========================================================================*/
/////////////// Read Lux from sensor //////////////////////
/*
For 0    < CH1/CH0 < 0.50 Lux = 0.0304  x CH0-0.062  x CH0 x ((CH1/CH0)1.4)
For 0.50 < CH1/CH0 < 0.61 Lux = 0.0224  x CH0-0.031  x CH1
For 0.61 < CH1/CH0 < 0.80 Lux = 0.0128  x CH0-0.0153 x CH1
For 0.80 < CH1/CH0 < 1.30 Lux = 0.00146 x CH0-0.00112x CH1
For        CH1/CH0 > 1.30 Lux = 0
 */
float TSL2561::lux()
{
    double lux0, lux1;
    double ratio;
    double dlux;

    _TSL2561AutoGain = false;
    _TSL2561IntegrationTime = TSL2561_INTEGRATIONTIME_13MS;
    _TSL2561Gain = TSL2561_GAIN_1X;
    /* Set default integration time and gain */
    setIntegrationTime(_TSL2561IntegrationTime);
    setGain(_TSL2561Gain);
    uint16_t x0, x1;
    getLuminosity(&x0, &x1);
    ch0 = x0;
    ch1 = x1;
    //printf("ch0 = %d, ch1 = %d\r\n", ch0, ch1);
    //printf("Integ. Time = %d, Gain %d\r\n",
    //     _TSL2561IntegrationTime, _TSL2561Gain);
    lux0 = (double)ch0;
    lux1 = (double)ch1;
    ratio = lux1 / lux0;
    read_timing_reg();
    lux0 *= (402.0/(double)integ_time);
    lux1 *= (402.0/(double)integ_time);
    lux0 /= gain;
    lux1 /= gain;
    //printf("Integ. Time = %f, Gain %d\r\n", integ_time, gain);
    if (ratio <= 0.5) {
        dlux = 0.03040 * lux0 - 0.06200 * lux0 * pow(ratio,1.4);
    } else if (ratio <= 0.61) {
        dlux = 0.02240 * lux0 - 0.03100 * lux1;
    } else if (ratio <= 0.80) {
        dlux = 0.01280 * lux0 - 0.01530 * lux1;
    } else if (ratio <= 1.30) {
        dlux = 0.00146 * lux0 - 0.00112 * lux1;
    } else {
        dlux = 0;
    }
    disable();
    return (float)dlux;
}

float TSL2561::lux_auto(void)
{
    uint32_t data;

    enableAutoRange(true);
    getEvent(&data);
    //printf("line:%d\r\n", __LINE__);
    return (float)data;
}

/**************************************************************************/
/*!
    @brief  Initializes I2C connection and settings.
    Attempts to determine if the sensor is contactable, then sets up a default
    integration time and gain. Then powers down the chip.
    @returns True if sensor is found and initialized, false otherwise.
*/
/**************************************************************************/
bool TSL2561::init()
{
    _TSL2561Initialised = false;
    _TSL2561AutoGain = false;
    _TSL2561IntegrationTime = TSL2561_INTEGRATIONTIME_13MS;
    _TSL2561Gain = TSL2561_GAIN_1X;

    /* Make sure we're actually connected */
    uint8_t x = read8(TSL2561_COMMAND_BIT | TSL2561_REGISTER_ID);
    if (x & 0xF0 != 0x50) { // ID code for TSL2561
        //printf("initialize error!\r\n");
        return false;
    }
    //printf("ID=0x%02x\r\n", x);
    _TSL2561Initialised = true;

    /* Set default integration time and gain */
    setIntegrationTime(_TSL2561IntegrationTime);
    setGain(_TSL2561Gain);
    read_timing_reg();

    /* Note: by default, the device is in power down mode on bootup */
    disable();

    t.reset();
    t.start();
    //printf("Integ. Time = %d, Gain %d\r\n",
    //         _TSL2561IntegrationTime, _TSL2561Gain);
    //printf("initialize finished!\r\n");
    x = read8(TSL2561_COMMAND_BIT | TSL2561_REGISTER_TIMING);
    //printf("TIMING REG=0x%02x\r\n", x);
    return true;
}

/**************************************************************************/
/*!
    @brief  Enables or disables the auto-gain settings when reading
            data from the sensor
    @param enable Set to true to enable, False to disable
*/
/**************************************************************************/
void TSL2561::enableAutoRange(bool enable)
{
    _TSL2561AutoGain = enable ? true : false;
}

/**************************************************************************/
/*!
    @brief      Sets the integration time for the TSL2561. Higher time means
                more light captured (better for low light conditions) but will
        take longer to run readings.
    @param time The amount of time we'd like to add up values
*/
/**************************************************************************/
void TSL2561::setIntegrationTime(TSL2561IntegrationTime_t time)
{
    /* Enable the device by setting the control bit to 0x03 */
    enable();
    /* Update the timing register */
    write8(TSL2561_COMMAND_BIT | TSL2561_REGISTER_TIMING, time | _TSL2561Gain);
    /* Update value placeholders */
    _TSL2561IntegrationTime = time;
    /* Turn the device off to save power */
    disable();
}

/**************************************************************************/
/*!
    @brief  Adjusts the gain on the TSL2561 (adjusts the sensitivity to light)
    @param gain The value we'd like to set the gain to
*/
/**************************************************************************/
void TSL2561::setGain(TSL2561Gain_t gain)
{
    /* Enable the device by setting the control bit to 0x03 */
    enable();
    /* Update the timing register */
    write8(TSL2561_COMMAND_BIT | TSL2561_REGISTER_TIMING, _TSL2561IntegrationTime | gain);
    /* Update value placeholders */
    _TSL2561Gain = gain;
    /* Turn the device off to save power */
    disable();
}

/**************************************************************************/
/*!
    @brief  Gets the broadband (mixed lighting) and IR only values from
            the TSL2561, adjusting gain if auto-gain is enabled
    @param  broadband Pointer to a uint16_t we will fill with a sensor
                      reading from the IR+visible light diode.
    @param  ir Pointer to a uint16_t we will fill with a sensor the
               IR-only light diode.
*/
/**************************************************************************/
void TSL2561::getLuminosity (uint16_t *broadband, uint16_t *ir)
{
    bool valid = false;

    /* If Auto gain disabled get a single reading and continue */
    if(!_TSL2561AutoGain) {
        //printf("Get data without Auto gain\r\n");
        getData (broadband, ir);
        return;
    }
    //printf("Get data with Auto gain\r\n");
    /* Read data until we find a valid range */
    bool _agcCheck = false;
    do {
        uint16_t _b, _ir;
        uint16_t _hi, _lo;
        TSL2561IntegrationTime_t _it = _TSL2561IntegrationTime;

        /* Get the hi/low threshold for the current integration time */
        switch(_it) {
            case TSL2561_INTEGRATIONTIME_13MS:
                _hi = TSL2561_AGC_THI_13MS;
                _lo = TSL2561_AGC_TLO_13MS;
                break;
            case TSL2561_INTEGRATIONTIME_101MS:
                _hi = TSL2561_AGC_THI_101MS;
                _lo = TSL2561_AGC_TLO_101MS;
                break;
            default:
                _hi = TSL2561_AGC_THI_402MS;
                _lo = TSL2561_AGC_TLO_402MS;
                break;
        }

        getData(&_b, &_ir);

        /* Run an auto-gain check if we haven't already done so ... */
        if (!_agcCheck) {
            if ((_b < _lo) && (_TSL2561Gain == TSL2561_GAIN_1X)) {
                /* Increase the gain and try again */
                setGain(TSL2561_GAIN_16X);
                /* Drop the previous conversion results */
                getData(&_b, &_ir);
                /* Set a flag to indicate we've adjusted the gain */
                _agcCheck = true;
            } else if ((_b > _hi) && (_TSL2561Gain == TSL2561_GAIN_16X)) {
                /* Drop gain to 1x and try again */
                setGain(TSL2561_GAIN_1X);
                /* Drop the previous conversion results */
                getData(&_b, &_ir);
                /* Set a flag to indicate we've adjusted the gain */
                _agcCheck = true;
            } else {
                /* Nothing to look at here, keep moving ....
                   Reading is either valid, or we're already at the chips limits */
                *broadband = _b;
                *ir = _ir;
                valid = true;
            }
        } else {
            /* If we've already adjusted the gain once, just return the new results.
               This avoids endless loops where a value is at one extreme pre-gain,
               and the the other extreme post-gain */
            *broadband = _b;
            *ir = _ir;
            valid = true;
        }
    } while (!valid);
}

/**************************************************************************/
/*!
    Enables the device
*/
/**************************************************************************/
void TSL2561::enable(void)
{
    /* Enable the device by setting the control bit to 0x03 */
    write8(TSL2561_COMMAND_BIT | TSL2561_REGISTER_CONTROL, TSL2561_CONTROL_POWERON);
}

/**************************************************************************/
/*!
    Disables the device (putting it in lower power sleep mode)
*/
/**************************************************************************/
void TSL2561::disable(void)
{
    /* Turn the device off to save power */
    write8(TSL2561_COMMAND_BIT | TSL2561_REGISTER_CONTROL, TSL2561_CONTROL_POWEROFF);
}

/**************************************************************************/
/*!
    Private function to read luminosity on both channels
*/
/**************************************************************************/
void TSL2561::getData (uint16_t *broadband, uint16_t *ir)
{
    /* Enable the device by setting the control bit to 0x03 */
    enable();

    /* Wait x ms for ADC to complete */
    switch (_TSL2561IntegrationTime) {
        case TSL2561_INTEGRATIONTIME_13MS:
            wait_ms(TSL2561_DELAY_INTTIME_13MS);
            break;
        case TSL2561_INTEGRATIONTIME_101MS:
            wait_ms(TSL2561_DELAY_INTTIME_101MS);
            break;
        default:
            wait_ms(TSL2561_DELAY_INTTIME_402MS);
            break;
    }

    /* Reads a two byte value from channel 0 (visible + infrared) */
    *broadband = read16(TSL2561_COMMAND_BIT | TSL2561_WORD_BIT | TSL2561_REGISTER_CHAN0_LOW);

    /* Reads a two byte value from channel 1 (infrared) */
    *ir = read16(TSL2561_COMMAND_BIT | TSL2561_WORD_BIT | TSL2561_REGISTER_CHAN1_LOW);

    /* Turn the device off to save power */
    disable();
}

/**************************************************************************/
/*!
    @brief  Converts the raw sensor values to the standard SI lux equivalent.
    @param  broadband The 16-bit sensor reading from the IR+visible light diode.
    @param  ir The 16-bit sensor reading from the IR-only light diode.
    @returns The integer Lux value we calcuated.
             Returns 0 if the sensor is saturated and the values are
             unreliable, or 65536 if the sensor is saturated.
*/
/**************************************************************************/
/**************************************************************************/
/*!

    Returns
*/
/**************************************************************************/
uint32_t TSL2561::calculateLux(uint16_t broadband, uint16_t ir)
{
    unsigned long chScale;
    unsigned long channel1;
    unsigned long channel0;

    //printf("line:%d\r\n", __LINE__);
    /* Make sure the sensor isn't saturated! */
    uint16_t clipThreshold;
    //printf("_TSL2561IntegrationTime=%d\r\n", _TSL2561IntegrationTime);
    switch (_TSL2561IntegrationTime) {
        case TSL2561_INTEGRATIONTIME_13MS:
            clipThreshold = TSL2561_CLIPPING_13MS;
            chScale = TSL2561_LUX_CHSCALE_TINT0;
            //printf("line:%d\r\n", __LINE__);
            break;
        case TSL2561_INTEGRATIONTIME_101MS:
            clipThreshold = TSL2561_CLIPPING_101MS;
            chScale = TSL2561_LUX_CHSCALE_TINT1;
            break;
        default:
            clipThreshold = TSL2561_CLIPPING_402MS;
            chScale = (1 << TSL2561_LUX_CHSCALE);
            break;
    }
    //printf("clipThreshold=%d\r\n", clipThreshold);
    /* Return 65536 lux if the sensor is saturated */
    if ((broadband > clipThreshold) || (ir > clipThreshold)) {
        //printf("line:%d\r\n", __LINE__);
        return 65536;
    }

    //printf("line:%d\r\n", __LINE__);
    //printf("chScale=%d\r\n", chScale);
    /* Scale for gain (1x or 16x) */
    if (!_TSL2561Gain) {
        chScale = chScale << 4;
    }
    //printf("chScale=%d\r\n", chScale);
    /* Scale the channel values */
    channel0 = (broadband * chScale) >> TSL2561_LUX_CHSCALE;
    channel1 = (ir * chScale) >> TSL2561_LUX_CHSCALE;
    //printf("channel0=%d, channel1=%d\r\n", channel0, channel1);

    /* Find the ratio of the channel values (Channel1/Channel0) */
    unsigned long ratio1 = 0;
    if (channel0 != 0) {
        ratio1 = (channel1 << (TSL2561_LUX_RATIOSCALE+1)) / channel0;
    }

    /* round the ratio value */
    long ratio = (ratio1 + 1) >> 1;
    //printf("ratio1=%d, ratio=%d\r\n", ratio1, ratio);
    unsigned int b, m;

#ifdef TSL2561_PACKAGE_CS
    //printf("line:%d\r\n", __LINE__);
    if ((ratio >= 0) && (ratio <= TSL2561_LUX_K1C)) {
        b=TSL2561_LUX_B1C;
        m=TSL2561_LUX_M1C;
    } else if (ratio <= TSL2561_LUX_K2C) {
        b=TSL2561_LUX_B2C;
        m=TSL2561_LUX_M2C;
    } else if (ratio <= TSL2561_LUX_K3C) {
        b=TSL2561_LUX_B3C;
        m=TSL2561_LUX_M3C;
    } else if (ratio <= TSL2561_LUX_K4C) {
        b=TSL2561_LUX_B4C;
        m=TSL2561_LUX_M4C;
    } else if (ratio <= TSL2561_LUX_K5C) {
        b=TSL2561_LUX_B5C;
        m=TSL2561_LUX_M5C;
    } else if (ratio <= TSL2561_LUX_K6C) {
        b=TSL2561_LUX_B6C;
        m=TSL2561_LUX_M6C;
    } else if (ratio <= TSL2561_LUX_K7C) {
        b=TSL2561_LUX_B7C;
        m=TSL2561_LUX_M7C;
    } else if (ratio > TSL2561_LUX_K8C) {
        b=TSL2561_LUX_B8C;
        m=TSL2561_LUX_M8C;
    }
#else
    //printf("line:%d\r\n", __LINE__);
    if ((ratio >= 0) && (ratio <= TSL2561_LUX_K1T)) {
        b=TSL2561_LUX_B1T;
        m=TSL2561_LUX_M1T;
    } else if (ratio <= TSL2561_LUX_K2T) {
        b=TSL2561_LUX_B2T;
        m=TSL2561_LUX_M2T;
    } else if (ratio <= TSL2561_LUX_K3T) {
        b=TSL2561_LUX_B3T;
        m=TSL2561_LUX_M3T;
    } else if (ratio <= TSL2561_LUX_K4T) {
        b=TSL2561_LUX_B4T;
        m=TSL2561_LUX_M4T;
    } else if (ratio <= TSL2561_LUX_K5T) {
        b=TSL2561_LUX_B5T;
        m=TSL2561_LUX_M5T;
    } else if (ratio <= TSL2561_LUX_K6T) {
        b=TSL2561_LUX_B6T;
        m=TSL2561_LUX_M6T;
    } else if (ratio <= TSL2561_LUX_K7T) {
        b=TSL2561_LUX_B7T;
        m=TSL2561_LUX_M7T;
    } else if (ratio > TSL2561_LUX_K8T) {
        b=TSL2561_LUX_B8T;
        m=TSL2561_LUX_M8T;
    }
#endif

    long temp;
    temp = ((channel0 * b) - (channel1 * m));

    /* Do not allow negative lux value */
    if (temp < 0L) {
        temp = 0;
    }
    //printf("line:%d\r\n", __LINE__);
    /* Round lsb (2^(LUX_SCALE-1)) */
    temp += (1 << (TSL2561_LUX_LUXSCALE-1));

    /* Strip off fractional portion */
    uint32_t lux = temp >> TSL2561_LUX_LUXSCALE;

    /* Signal I2C had no errors */
    //printf("Lux=%d\r\n", lux);
    return lux;
}

/**************************************************************************/
/*!
    @brief  Gets the most recent sensor event
    @param  event Pointer to a sensor_event_t type that will be filled
                  with the lux value, timestamp, data type and sensor ID.
    @returns True if sensor reading is between 0 and 65535 lux,
             false if sensor is saturated
*/
/**************************************************************************/
bool TSL2561::getEvent(uint32_t *event)
{
    uint16_t broadband, ir;

    /* Calculate the actual lux value */
    getLuminosity(&broadband, &ir);
    //printf("broadband=%d, ir=%d\r\n", broadband, ir);
    *event = calculateLux(broadband, ir);
    //printf("LUX=%f\r\n", event->light);
    //printf("LUX=%d\r\n", *event);
    //printf("line:%d\r\n", __LINE__);
    if (*event == 65536) {
        //printf("line:%d\r\n", __LINE__);
        return false;
    }
    //printf("line:%d\r\n", __LINE__);
    return true;
}

/**************************************************************************/
/*!
    @brief  Gets the sensor_t data
    @param  sensor A pointer to a sensor_t structure that we will fill with
                   details about the TSL2561 and its capabilities
*/
/**************************************************************************/
void TSL2561::getSensor(sensor_t *sensor)
{
    /* Insert the sensor name in the fixed length char array */
    //sensor->version     = 1;
    sensor->min_delay   = 0;
    sensor->max_value   = 17000.0;  /* Based on trial and error ... confirm! */
    sensor->min_value   = 1.0;
    sensor->resolution  = 1.0;
}

/*========================================================================*/
/*                          PRIVATE FUNCTIONS                             */
/*========================================================================*/

/**************************************************************************/
/*!
    @brief  Writes a register and an 8 bit value over I2C
    @param  reg I2C register to write the value to
    @param  value The 8-bit value we're writing to the register
*/
/**************************************************************************/
void TSL2561::write8 (uint8_t reg, uint8_t value)
{
    dt[0] = reg;
    dt[1] = value;
    _i2c.write((int)_addr, (char *)dt, 2, false);
}

/**************************************************************************/
/*!
    @brief  Reads an 8 bit value over I2C
    @param  reg I2C register to read from
    @returns 8-bit value containing single byte data read
*/
/**************************************************************************/
uint8_t TSL2561::read8(uint8_t reg)
{
    dt[0] = reg;
    _i2c.write((int)_addr, (char *)dt, 1, true);
    _i2c.read(_addr, (char *)dt, 1, false);
    return dt[0];
}

/**************************************************************************/
/*!
    @brief  Reads a 16 bit values over I2C
    @param  reg I2C register to read from
    @returns 16-bit value containing 2-byte data read
*/
/**************************************************************************/
uint16_t TSL2561::read16(uint8_t reg)
{
    dt[0] = reg;
    _i2c.write((int)_addr, (char *)dt, 1, true);
    _i2c.read(_addr, (char *)dt, 2, false);
    uint16_t data = ((uint16_t)dt[1] << 8) + dt[0];
    return data;
}

/*========================================================================*/
/*   JH1PJL Original Functions  -> Keep compacibility for older revision  */
/*========================================================================*/

/////////////// Timing Register ///////////////////////////
uint8_t TSL2561::set_timing_reg(uint8_t parameter)
{
    enable();
    write8(TSL2561_COMMAND_BIT | TSL2561_REGISTER_TIMING, parameter);
    return read8(TSL2561_COMMAND_BIT | TSL2561_REGISTER_TIMING);
}

uint8_t TSL2561::read_timing_reg(void)
{
    uint8_t i, data;

    data = read8(TSL2561_COMMAND_BIT | TSL2561_REGISTER_TIMING);
    if (data & TSL2561_GAIN_16X) {
        gain = 16;
        _TSL2561Gain = TSL2561_GAIN_16X;
    } else {
        gain = 1;
        _TSL2561Gain = TSL2561_GAIN_1X;
    }
    i = data & 0x3;
    switch (i) {
        case 0:
            integ_time = 13.7;
            _TSL2561IntegrationTime = TSL2561_INTEGRATIONTIME_13MS;
            break;
        case 1:
            integ_time = 101.0;
            _TSL2561IntegrationTime = TSL2561_INTEGRATIONTIME_101MS;
            break;
        case 2:
            integ_time = 402.0;
            _TSL2561IntegrationTime = TSL2561_INTEGRATIONTIME_402MS;
            break;
        default:
            integ_time = 0;
            _TSL2561IntegrationTime = TSL2561_INTEGRATIONTIME_13MS;
            break;
    }
    return dt[0];
}

/////////////// ID ////////////////////////////////////////
uint8_t TSL2561::read_ID()
{
    id_number = read8(TSL2561_COMMAND_BIT | TSL2561_REGISTER_ID);
    return id_number;
}

uint8_t TSL2561::who_am_i()
{
    read_ID();
    if ((id_number >> 4) == I_AM_TSL2561CS) {
        return 1;
    } else if ((id_number >> 4) == I_AM_TSL2561T_FN_CL) {
        return 1;
    } else {
        return 0;
    }
}

/////////////// Power ON/OFF //////////////////////////////
void TSL2561::power_up()
{
    enable();
}

void TSL2561::power_down()
{
    disable();
}

/////////////// I2C Freq. /////////////////////////////////
void TSL2561::frequency(int hz)
{
    _i2c.frequency(hz);
}