Adafruit_TSL2561_U.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. /**************************************************************************/
  2. /*!
  3. @file Adafruit_TSL2561.cpp
  4. @author K.Townsend (Adafruit Industries)
  5. @license BSD (see license.txt)
  6. Driver for the TSL2561 digital luminosity (light) sensors.
  7. Pick one up at http://www.adafruit.com/products/439
  8. Adafruit invests time and resources providing this open source code,
  9. please support Adafruit and open-source hardware by purchasing
  10. products from Adafruit!
  11. @section HISTORY
  12. v2.0 - Rewrote driver for Adafruit_Sensor and Auto-Gain support, and
  13. added lux clipping check (returns 0 lux on sensor saturation)
  14. v1.0 - First release (previously TSL2561)
  15. */
  16. /**************************************************************************/
  17. #if defined(__AVR__)
  18. #include <avr/pgmspace.h>
  19. #include <util/delay.h>
  20. #else
  21. #include "pgmspace.h"
  22. #endif
  23. #include <stdlib.h>
  24. #include "Adafruit_TSL2561_U.h"
  25. #define TSL2561_DELAY_INTTIME_13MS (15)
  26. #define TSL2561_DELAY_INTTIME_101MS (120)
  27. #define TSL2561_DELAY_INTTIME_402MS (450)
  28. /*========================================================================*/
  29. /* PRIVATE FUNCTIONS */
  30. /*========================================================================*/
  31. /**************************************************************************/
  32. /*!
  33. @brief Writes a register and an 8 bit value over I2C
  34. */
  35. /**************************************************************************/
  36. void Adafruit_TSL2561_Unified::write8 (uint8_t reg, uint32_t value)
  37. {
  38. Wire.beginTransmission(_addr);
  39. Wire.write(reg);
  40. Wire.write(value & 0xFF);
  41. Wire.endTransmission();
  42. }
  43. /**************************************************************************/
  44. /*!
  45. @brief Reads an 8 bit value over I2C
  46. */
  47. /**************************************************************************/
  48. uint8_t Adafruit_TSL2561_Unified::read8(uint8_t reg)
  49. {
  50. Wire.beginTransmission(_addr);
  51. #if ARDUINO >= 100
  52. Wire.write(reg);
  53. #else
  54. Wire.send(reg);
  55. #endif
  56. Wire.endTransmission();
  57. Wire.requestFrom(_addr, 1);
  58. #if ARDUINO >= 100
  59. return Wire.read();
  60. #else
  61. return Wire.receive();
  62. #endif
  63. }
  64. /**************************************************************************/
  65. /*!
  66. @brief Reads a 16 bit values over I2C
  67. */
  68. /**************************************************************************/
  69. uint16_t Adafruit_TSL2561_Unified::read16(uint8_t reg)
  70. {
  71. uint16_t x; uint16_t t;
  72. Wire.beginTransmission(_addr);
  73. #if ARDUINO >= 100
  74. Wire.write(reg);
  75. #else
  76. Wire.send(reg);
  77. #endif
  78. Wire.endTransmission();
  79. Wire.requestFrom(_addr, 2);
  80. #if ARDUINO >= 100
  81. t = Wire.read();
  82. x = Wire.read();
  83. #else
  84. t = Wire.receive();
  85. x = Wire.receive();
  86. #endif
  87. x <<= 8;
  88. x |= t;
  89. return x;
  90. }
  91. /**************************************************************************/
  92. /*!
  93. Enables the device
  94. */
  95. /**************************************************************************/
  96. void Adafruit_TSL2561_Unified::enable(void)
  97. {
  98. /* Enable the device by setting the control bit to 0x03 */
  99. write8(TSL2561_COMMAND_BIT | TSL2561_REGISTER_CONTROL, TSL2561_CONTROL_POWERON);
  100. }
  101. /**************************************************************************/
  102. /*!
  103. Disables the device (putting it in lower power sleep mode)
  104. */
  105. /**************************************************************************/
  106. void Adafruit_TSL2561_Unified::disable(void)
  107. {
  108. /* Turn the device off to save power */
  109. write8(TSL2561_COMMAND_BIT | TSL2561_REGISTER_CONTROL, TSL2561_CONTROL_POWEROFF);
  110. }
  111. /**************************************************************************/
  112. /*!
  113. Private function to read luminosity on both channels
  114. */
  115. /**************************************************************************/
  116. void Adafruit_TSL2561_Unified::getData (uint16_t *broadband, uint16_t *ir)
  117. {
  118. /* Enable the device by setting the control bit to 0x03 */
  119. enable();
  120. /* Wait x ms for ADC to complete */
  121. switch (_tsl2561IntegrationTime)
  122. {
  123. case TSL2561_INTEGRATIONTIME_13MS:
  124. delay(TSL2561_DELAY_INTTIME_13MS); // KTOWN: Was 14ms
  125. break;
  126. case TSL2561_INTEGRATIONTIME_101MS:
  127. delay(TSL2561_DELAY_INTTIME_101MS); // KTOWN: Was 102ms
  128. break;
  129. default:
  130. delay(TSL2561_DELAY_INTTIME_402MS); // KTOWN: Was 403ms
  131. break;
  132. }
  133. /* Reads a two byte value from channel 0 (visible + infrared) */
  134. *broadband = read16(TSL2561_COMMAND_BIT | TSL2561_WORD_BIT | TSL2561_REGISTER_CHAN0_LOW);
  135. /* Reads a two byte value from channel 1 (infrared) */
  136. *ir = read16(TSL2561_COMMAND_BIT | TSL2561_WORD_BIT | TSL2561_REGISTER_CHAN1_LOW);
  137. /* Turn the device off to save power */
  138. disable();
  139. }
  140. /*========================================================================*/
  141. /* CONSTRUCTORS */
  142. /*========================================================================*/
  143. /**************************************************************************/
  144. /*!
  145. Constructor
  146. */
  147. /**************************************************************************/
  148. Adafruit_TSL2561_Unified::Adafruit_TSL2561_Unified(uint8_t addr, int32_t sensorID)
  149. {
  150. _addr = addr;
  151. _tsl2561Initialised = false;
  152. _tsl2561AutoGain = false;
  153. _tsl2561IntegrationTime = TSL2561_INTEGRATIONTIME_13MS;
  154. _tsl2561Gain = TSL2561_GAIN_1X;
  155. _tsl2561SensorID = sensorID;
  156. }
  157. /*========================================================================*/
  158. /* PUBLIC FUNCTIONS */
  159. /*========================================================================*/
  160. /**************************************************************************/
  161. /*!
  162. Initializes I2C and configures the sensor (call this function before
  163. doing anything else)
  164. */
  165. /**************************************************************************/
  166. boolean Adafruit_TSL2561_Unified::begin(void)
  167. {
  168. Wire.begin();
  169. /* Make sure we're actually connected */
  170. uint8_t x = read8(TSL2561_REGISTER_ID);
  171. if (!(x & 0x0A))
  172. {
  173. return false;
  174. }
  175. _tsl2561Initialised = true;
  176. /* Set default integration time and gain */
  177. setIntegrationTime(_tsl2561IntegrationTime);
  178. setGain(_tsl2561Gain);
  179. /* Note: by default, the device is in power down mode on bootup */
  180. disable();
  181. return true;
  182. }
  183. /**************************************************************************/
  184. /*!
  185. @brief Enables or disables the auto-gain settings when reading
  186. data from the sensor
  187. */
  188. /**************************************************************************/
  189. void Adafruit_TSL2561_Unified::enableAutoRange(bool enable)
  190. {
  191. _tsl2561AutoGain = enable ? true : false;
  192. }
  193. /**************************************************************************/
  194. /*!
  195. Sets the integration time for the TSL2561
  196. */
  197. /**************************************************************************/
  198. void Adafruit_TSL2561_Unified::setIntegrationTime(tsl2561IntegrationTime_t time)
  199. {
  200. if (!_tsl2561Initialised) begin();
  201. /* Enable the device by setting the control bit to 0x03 */
  202. enable();
  203. /* Update the timing register */
  204. write8(TSL2561_COMMAND_BIT | TSL2561_REGISTER_TIMING, time | _tsl2561Gain);
  205. /* Update value placeholders */
  206. _tsl2561IntegrationTime = time;
  207. /* Turn the device off to save power */
  208. disable();
  209. }
  210. /**************************************************************************/
  211. /*!
  212. Adjusts the gain on the TSL2561 (adjusts the sensitivity to light)
  213. */
  214. /**************************************************************************/
  215. void Adafruit_TSL2561_Unified::setGain(tsl2561Gain_t gain)
  216. {
  217. if (!_tsl2561Initialised) begin();
  218. /* Enable the device by setting the control bit to 0x03 */
  219. enable();
  220. /* Update the timing register */
  221. write8(TSL2561_COMMAND_BIT | TSL2561_REGISTER_TIMING, _tsl2561IntegrationTime | gain);
  222. /* Update value placeholders */
  223. _tsl2561Gain = gain;
  224. /* Turn the device off to save power */
  225. disable();
  226. }
  227. /**************************************************************************/
  228. /*!
  229. @brief Gets the broadband (mixed lighting) and IR only values from
  230. the TSL2561, adjusting gain if auto-gain is enabled
  231. */
  232. /**************************************************************************/
  233. void Adafruit_TSL2561_Unified::getLuminosity (uint16_t *broadband, uint16_t *ir)
  234. {
  235. bool valid = false;
  236. if (!_tsl2561Initialised) begin();
  237. /* If Auto gain disabled get a single reading and continue */
  238. if(!_tsl2561AutoGain)
  239. {
  240. getData (broadband, ir);
  241. return;
  242. }
  243. /* Read data until we find a valid range */
  244. bool _agcCheck = false;
  245. do
  246. {
  247. uint16_t _b, _ir;
  248. uint16_t _hi, _lo;
  249. tsl2561IntegrationTime_t _it = _tsl2561IntegrationTime;
  250. /* Get the hi/low threshold for the current integration time */
  251. switch(_it)
  252. {
  253. case TSL2561_INTEGRATIONTIME_13MS:
  254. _hi = TSL2561_AGC_THI_13MS;
  255. _lo = TSL2561_AGC_TLO_13MS;
  256. break;
  257. case TSL2561_INTEGRATIONTIME_101MS:
  258. _hi = TSL2561_AGC_THI_101MS;
  259. _lo = TSL2561_AGC_TLO_101MS;
  260. break;
  261. default:
  262. _hi = TSL2561_AGC_THI_402MS;
  263. _lo = TSL2561_AGC_TLO_402MS;
  264. break;
  265. }
  266. getData(&_b, &_ir);
  267. /* Run an auto-gain check if we haven't already done so ... */
  268. if (!_agcCheck)
  269. {
  270. if ((_b < _lo) && (_tsl2561Gain == TSL2561_GAIN_1X))
  271. {
  272. /* Increase the gain and try again */
  273. setGain(TSL2561_GAIN_16X);
  274. /* Drop the previous conversion results */
  275. getData(&_b, &_ir);
  276. /* Set a flag to indicate we've adjusted the gain */
  277. _agcCheck = true;
  278. }
  279. else if ((_b > _hi) && (_tsl2561Gain == TSL2561_GAIN_16X))
  280. {
  281. /* Drop gain to 1x and try again */
  282. setGain(TSL2561_GAIN_1X);
  283. /* Drop the previous conversion results */
  284. getData(&_b, &_ir);
  285. /* Set a flag to indicate we've adjusted the gain */
  286. _agcCheck = true;
  287. }
  288. else
  289. {
  290. /* Nothing to look at here, keep moving ....
  291. Reading is either valid, or we're already at the chips limits */
  292. *broadband = _b;
  293. *ir = _ir;
  294. valid = true;
  295. }
  296. }
  297. else
  298. {
  299. /* If we've already adjusted the gain once, just return the new results.
  300. This avoids endless loops where a value is at one extreme pre-gain,
  301. and the the other extreme post-gain */
  302. *broadband = _b;
  303. *ir = _ir;
  304. valid = true;
  305. }
  306. } while (!valid);
  307. }
  308. /**************************************************************************/
  309. /*!
  310. Converts the raw sensor values to the standard SI lux equivalent.
  311. Returns 0 if the sensor is saturated and the values are unreliable.
  312. */
  313. /**************************************************************************/
  314. uint32_t Adafruit_TSL2561_Unified::calculateLux(uint16_t broadband, uint16_t ir)
  315. {
  316. unsigned long chScale;
  317. unsigned long channel1;
  318. unsigned long channel0;
  319. /* Make sure the sensor isn't saturated! */
  320. uint16_t clipThreshold;
  321. switch (_tsl2561IntegrationTime)
  322. {
  323. case TSL2561_INTEGRATIONTIME_13MS:
  324. clipThreshold = TSL2561_CLIPPING_13MS;
  325. break;
  326. case TSL2561_INTEGRATIONTIME_101MS:
  327. clipThreshold = TSL2561_CLIPPING_101MS;
  328. break;
  329. default:
  330. clipThreshold = TSL2561_CLIPPING_402MS;
  331. break;
  332. }
  333. /* Return 65536 lux if the sensor is saturated */
  334. if ((broadband > clipThreshold) || (ir > clipThreshold))
  335. {
  336. return 65536;
  337. }
  338. /* Get the correct scale depending on the intergration time */
  339. switch (_tsl2561IntegrationTime)
  340. {
  341. case TSL2561_INTEGRATIONTIME_13MS:
  342. chScale = TSL2561_LUX_CHSCALE_TINT0;
  343. break;
  344. case TSL2561_INTEGRATIONTIME_101MS:
  345. chScale = TSL2561_LUX_CHSCALE_TINT1;
  346. break;
  347. default: /* No scaling ... integration time = 402ms */
  348. chScale = (1 << TSL2561_LUX_CHSCALE);
  349. break;
  350. }
  351. /* Scale for gain (1x or 16x) */
  352. if (!_tsl2561Gain) chScale = chScale << 4;
  353. /* Scale the channel values */
  354. channel0 = (broadband * chScale) >> TSL2561_LUX_CHSCALE;
  355. channel1 = (ir * chScale) >> TSL2561_LUX_CHSCALE;
  356. /* Find the ratio of the channel values (Channel1/Channel0) */
  357. unsigned long ratio1 = 0;
  358. if (channel0 != 0) ratio1 = (channel1 << (TSL2561_LUX_RATIOSCALE+1)) / channel0;
  359. /* round the ratio value */
  360. unsigned long ratio = (ratio1 + 1) >> 1;
  361. unsigned int b, m;
  362. #ifdef TSL2561_PACKAGE_CS
  363. if ((ratio >= 0) && (ratio <= TSL2561_LUX_K1C))
  364. {b=TSL2561_LUX_B1C; m=TSL2561_LUX_M1C;}
  365. else if (ratio <= TSL2561_LUX_K2C)
  366. {b=TSL2561_LUX_B2C; m=TSL2561_LUX_M2C;}
  367. else if (ratio <= TSL2561_LUX_K3C)
  368. {b=TSL2561_LUX_B3C; m=TSL2561_LUX_M3C;}
  369. else if (ratio <= TSL2561_LUX_K4C)
  370. {b=TSL2561_LUX_B4C; m=TSL2561_LUX_M4C;}
  371. else if (ratio <= TSL2561_LUX_K5C)
  372. {b=TSL2561_LUX_B5C; m=TSL2561_LUX_M5C;}
  373. else if (ratio <= TSL2561_LUX_K6C)
  374. {b=TSL2561_LUX_B6C; m=TSL2561_LUX_M6C;}
  375. else if (ratio <= TSL2561_LUX_K7C)
  376. {b=TSL2561_LUX_B7C; m=TSL2561_LUX_M7C;}
  377. else if (ratio > TSL2561_LUX_K8C)
  378. {b=TSL2561_LUX_B8C; m=TSL2561_LUX_M8C;}
  379. #else
  380. if ((ratio >= 0) && (ratio <= TSL2561_LUX_K1T))
  381. {b=TSL2561_LUX_B1T; m=TSL2561_LUX_M1T;}
  382. else if (ratio <= TSL2561_LUX_K2T)
  383. {b=TSL2561_LUX_B2T; m=TSL2561_LUX_M2T;}
  384. else if (ratio <= TSL2561_LUX_K3T)
  385. {b=TSL2561_LUX_B3T; m=TSL2561_LUX_M3T;}
  386. else if (ratio <= TSL2561_LUX_K4T)
  387. {b=TSL2561_LUX_B4T; m=TSL2561_LUX_M4T;}
  388. else if (ratio <= TSL2561_LUX_K5T)
  389. {b=TSL2561_LUX_B5T; m=TSL2561_LUX_M5T;}
  390. else if (ratio <= TSL2561_LUX_K6T)
  391. {b=TSL2561_LUX_B6T; m=TSL2561_LUX_M6T;}
  392. else if (ratio <= TSL2561_LUX_K7T)
  393. {b=TSL2561_LUX_B7T; m=TSL2561_LUX_M7T;}
  394. else if (ratio > TSL2561_LUX_K8T)
  395. {b=TSL2561_LUX_B8T; m=TSL2561_LUX_M8T;}
  396. #endif
  397. unsigned long temp;
  398. temp = ((channel0 * b) - (channel1 * m));
  399. /* Do not allow negative lux value */
  400. if (temp < 0) temp = 0;
  401. /* Round lsb (2^(LUX_SCALE-1)) */
  402. temp += (1 << (TSL2561_LUX_LUXSCALE-1));
  403. /* Strip off fractional portion */
  404. uint32_t lux = temp >> TSL2561_LUX_LUXSCALE;
  405. /* Signal I2C had no errors */
  406. return lux;
  407. }
  408. /**************************************************************************/
  409. /*!
  410. @brief Gets the most recent sensor event
  411. returns true if sensor reading is between 0 and 65535 lux
  412. returns false if sensor is saturated
  413. */
  414. /**************************************************************************/
  415. bool Adafruit_TSL2561_Unified::getEvent(sensors_event_t *event)
  416. {
  417. uint16_t broadband, ir;
  418. /* Clear the event */
  419. memset(event, 0, sizeof(sensors_event_t));
  420. event->version = sizeof(sensors_event_t);
  421. event->sensor_id = _tsl2561SensorID;
  422. event->type = SENSOR_TYPE_LIGHT;
  423. event->timestamp = millis();
  424. /* Calculate the actual lux value */
  425. getLuminosity(&broadband, &ir);
  426. event->light = calculateLux(broadband, ir);
  427. if (event->light == 65536) {
  428. return false;
  429. }
  430. return true;
  431. }
  432. /**************************************************************************/
  433. /*!
  434. @brief Gets the sensor_t data
  435. */
  436. /**************************************************************************/
  437. void Adafruit_TSL2561_Unified::getSensor(sensor_t *sensor)
  438. {
  439. /* Clear the sensor_t object */
  440. memset(sensor, 0, sizeof(sensor_t));
  441. /* Insert the sensor name in the fixed length char array */
  442. strncpy (sensor->name, "TSL2561", sizeof(sensor->name) - 1);
  443. sensor->name[sizeof(sensor->name)- 1] = 0;
  444. sensor->version = 1;
  445. sensor->sensor_id = _tsl2561SensorID;
  446. sensor->type = SENSOR_TYPE_LIGHT;
  447. sensor->min_delay = 0;
  448. sensor->max_value = 17000.0; /* Based on trial and error ... confirm! */
  449. sensor->min_value = 0.0;
  450. sensor->resolution = 1.0;
  451. }