123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515 |
- /**************************************************************************/
- /*!
- @file Adafruit_TSL2561.cpp
- @author K.Townsend (Adafruit Industries)
- @license BSD (see license.txt)
- Driver for the TSL2561 digital luminosity (light) sensors.
- Pick one up at http://www.adafruit.com/products/439
- Adafruit invests time and resources providing this open source code,
- please support Adafruit and open-source hardware by purchasing
- products from Adafruit!
- @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)
- */
- /**************************************************************************/
- #if defined(__AVR__)
- #include <avr/pgmspace.h>
- #include <util/delay.h>
- #else
- #include "pgmspace.h"
- #endif
- #include <stdlib.h>
- #include "Adafruit_TSL2561_U.h"
- #define TSL2561_DELAY_INTTIME_13MS (15)
- #define TSL2561_DELAY_INTTIME_101MS (120)
- #define TSL2561_DELAY_INTTIME_402MS (450)
- /*========================================================================*/
- /* PRIVATE FUNCTIONS */
- /*========================================================================*/
- /**************************************************************************/
- /*!
- @brief Writes a register and an 8 bit value over I2C
- */
- /**************************************************************************/
- void Adafruit_TSL2561_Unified::write8 (uint8_t reg, uint32_t value)
- {
- Wire.beginTransmission(_addr);
- Wire.write(reg);
- Wire.write(value & 0xFF);
- Wire.endTransmission();
- }
- /**************************************************************************/
- /*!
- @brief Reads an 8 bit value over I2C
- */
- /**************************************************************************/
- uint8_t Adafruit_TSL2561_Unified::read8(uint8_t reg)
- {
- Wire.beginTransmission(_addr);
- #if ARDUINO >= 100
- Wire.write(reg);
- #else
- Wire.send(reg);
- #endif
- Wire.endTransmission();
- Wire.requestFrom(_addr, 1);
- #if ARDUINO >= 100
- return Wire.read();
- #else
- return Wire.receive();
- #endif
- }
- /**************************************************************************/
- /*!
- @brief Reads a 16 bit values over I2C
- */
- /**************************************************************************/
- uint16_t Adafruit_TSL2561_Unified::read16(uint8_t reg)
- {
- uint16_t x; uint16_t t;
- Wire.beginTransmission(_addr);
- #if ARDUINO >= 100
- Wire.write(reg);
- #else
- Wire.send(reg);
- #endif
- Wire.endTransmission();
- Wire.requestFrom(_addr, 2);
- #if ARDUINO >= 100
- t = Wire.read();
- x = Wire.read();
- #else
- t = Wire.receive();
- x = Wire.receive();
- #endif
- x <<= 8;
- x |= t;
- return x;
- }
- /**************************************************************************/
- /*!
- Enables the device
- */
- /**************************************************************************/
- void Adafruit_TSL2561_Unified::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 Adafruit_TSL2561_Unified::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 Adafruit_TSL2561_Unified::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:
- delay(TSL2561_DELAY_INTTIME_13MS); // KTOWN: Was 14ms
- break;
- case TSL2561_INTEGRATIONTIME_101MS:
- delay(TSL2561_DELAY_INTTIME_101MS); // KTOWN: Was 102ms
- break;
- default:
- delay(TSL2561_DELAY_INTTIME_402MS); // KTOWN: Was 403ms
- 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();
- }
- /*========================================================================*/
- /* CONSTRUCTORS */
- /*========================================================================*/
- /**************************************************************************/
- /*!
- Constructor
- */
- /**************************************************************************/
- Adafruit_TSL2561_Unified::Adafruit_TSL2561_Unified(uint8_t addr, int32_t sensorID)
- {
- _addr = addr;
- _tsl2561Initialised = false;
- _tsl2561AutoGain = false;
- _tsl2561IntegrationTime = TSL2561_INTEGRATIONTIME_13MS;
- _tsl2561Gain = TSL2561_GAIN_1X;
- _tsl2561SensorID = sensorID;
- }
- /*========================================================================*/
- /* PUBLIC FUNCTIONS */
- /*========================================================================*/
- /**************************************************************************/
- /*!
- Initializes I2C and configures the sensor (call this function before
- doing anything else)
- */
- /**************************************************************************/
- boolean Adafruit_TSL2561_Unified::begin(void)
- {
- Wire.begin();
- /* Make sure we're actually connected */
- uint8_t x = read8(TSL2561_REGISTER_ID);
- if (!(x & 0x0A))
- {
- return false;
- }
- _tsl2561Initialised = true;
- /* Set default integration time and gain */
- setIntegrationTime(_tsl2561IntegrationTime);
- setGain(_tsl2561Gain);
- /* Note: by default, the device is in power down mode on bootup */
- disable();
- return true;
- }
-
- /**************************************************************************/
- /*!
- @brief Enables or disables the auto-gain settings when reading
- data from the sensor
- */
- /**************************************************************************/
- void Adafruit_TSL2561_Unified::enableAutoRange(bool enable)
- {
- _tsl2561AutoGain = enable ? true : false;
- }
- /**************************************************************************/
- /*!
- Sets the integration time for the TSL2561
- */
- /**************************************************************************/
- void Adafruit_TSL2561_Unified::setIntegrationTime(tsl2561IntegrationTime_t time)
- {
- if (!_tsl2561Initialised) begin();
- /* 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();
- }
- /**************************************************************************/
- /*!
- Adjusts the gain on the TSL2561 (adjusts the sensitivity to light)
- */
- /**************************************************************************/
- void Adafruit_TSL2561_Unified::setGain(tsl2561Gain_t gain)
- {
- if (!_tsl2561Initialised) begin();
- /* 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
- */
- /**************************************************************************/
- void Adafruit_TSL2561_Unified::getLuminosity (uint16_t *broadband, uint16_t *ir)
- {
- bool valid = false;
- if (!_tsl2561Initialised) begin();
- /* If Auto gain disabled get a single reading and continue */
- if(!_tsl2561AutoGain)
- {
- getData (broadband, ir);
- return;
- }
- /* 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);
- }
- /**************************************************************************/
- /*!
- Converts the raw sensor values to the standard SI lux equivalent.
- Returns 0 if the sensor is saturated and the values are unreliable.
- */
- /**************************************************************************/
- uint32_t Adafruit_TSL2561_Unified::calculateLux(uint16_t broadband, uint16_t ir)
- {
- unsigned long chScale;
- unsigned long channel1;
- unsigned long channel0;
-
- /* Make sure the sensor isn't saturated! */
- uint16_t clipThreshold;
- switch (_tsl2561IntegrationTime)
- {
- case TSL2561_INTEGRATIONTIME_13MS:
- clipThreshold = TSL2561_CLIPPING_13MS;
- break;
- case TSL2561_INTEGRATIONTIME_101MS:
- clipThreshold = TSL2561_CLIPPING_101MS;
- break;
- default:
- clipThreshold = TSL2561_CLIPPING_402MS;
- break;
- }
- /* Return 65536 lux if the sensor is saturated */
- if ((broadband > clipThreshold) || (ir > clipThreshold))
- {
- return 65536;
- }
- /* Get the correct scale depending on the intergration time */
- switch (_tsl2561IntegrationTime)
- {
- case TSL2561_INTEGRATIONTIME_13MS:
- chScale = TSL2561_LUX_CHSCALE_TINT0;
- break;
- case TSL2561_INTEGRATIONTIME_101MS:
- chScale = TSL2561_LUX_CHSCALE_TINT1;
- break;
- default: /* No scaling ... integration time = 402ms */
- chScale = (1 << TSL2561_LUX_CHSCALE);
- break;
- }
- /* Scale for gain (1x or 16x) */
- if (!_tsl2561Gain) chScale = chScale << 4;
- /* Scale the channel values */
- channel0 = (broadband * chScale) >> TSL2561_LUX_CHSCALE;
- channel1 = (ir * chScale) >> TSL2561_LUX_CHSCALE;
- /* 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 */
- unsigned long ratio = (ratio1 + 1) >> 1;
- unsigned int b, m;
- #ifdef TSL2561_PACKAGE_CS
- 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
- 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
- unsigned long temp;
- temp = ((channel0 * b) - (channel1 * m));
- /* Do not allow negative lux value */
- if (temp < 0) temp = 0;
- /* 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 */
- return lux;
- }
- /**************************************************************************/
- /*!
- @brief Gets the most recent sensor event
- returns true if sensor reading is between 0 and 65535 lux
- returns false if sensor is saturated
- */
- /**************************************************************************/
- bool Adafruit_TSL2561_Unified::getEvent(sensors_event_t *event)
- {
- uint16_t broadband, ir;
-
- /* Clear the event */
- memset(event, 0, sizeof(sensors_event_t));
-
- event->version = sizeof(sensors_event_t);
- event->sensor_id = _tsl2561SensorID;
- event->type = SENSOR_TYPE_LIGHT;
- event->timestamp = millis();
- /* Calculate the actual lux value */
- getLuminosity(&broadband, &ir);
- event->light = calculateLux(broadband, ir);
-
- if (event->light == 65536) {
- return false;
- }
- return true;
- }
- /**************************************************************************/
- /*!
- @brief Gets the sensor_t data
- */
- /**************************************************************************/
- void Adafruit_TSL2561_Unified::getSensor(sensor_t *sensor)
- {
- /* Clear the sensor_t object */
- memset(sensor, 0, sizeof(sensor_t));
- /* Insert the sensor name in the fixed length char array */
- strncpy (sensor->name, "TSL2561", sizeof(sensor->name) - 1);
- sensor->name[sizeof(sensor->name)- 1] = 0;
- sensor->version = 1;
- sensor->sensor_id = _tsl2561SensorID;
- sensor->type = SENSOR_TYPE_LIGHT;
- sensor->min_delay = 0;
- sensor->max_value = 17000.0; /* Based on trial and error ... confirm! */
- sensor->min_value = 0.0;
- sensor->resolution = 1.0;
- }
|