APDS9960.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. /**************************************************************************/
  2. /*!
  3. @file APDS9960.cpp
  4. @author Ladyada, Dean Miller (Adafruit Industries)
  5. @section LICENSE
  6. Software License Agreement (BSD License)
  7. Copyright (c) 2017, Adafruit Industries
  8. All rights reserved.
  9. Redistribution and use in source and binary forms, with or without
  10. modification, are permitted provided that the following conditions are met:
  11. 1. Redistributions of source code must retain the above copyright
  12. notice, this list of conditions and the following disclaimer.
  13. 2. Redistributions in binary form must reproduce the above copyright
  14. notice, this list of conditions and the following disclaimer in the
  15. documentation and/or other materials provided with the distribution.
  16. 3. Neither the name of the copyright holders nor the
  17. names of its contributors may be used to endorse or promote products
  18. derived from this software without specific prior written permission.
  19. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
  20. EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  21. WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  22. DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
  23. DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  24. (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  25. LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  26. ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  28. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29. */
  30. /**************************************************************************/
  31. #ifdef __AVR
  32. #include <avr/pgmspace.h>
  33. #elif defined(ESP8266)
  34. #include <pgmspace.h>
  35. #endif
  36. #include <stdlib.h>
  37. #include <math.h>
  38. #include "APDS9960.h"
  39. /*========================================================================*/
  40. /* PRIVATE FUNCTIONS */
  41. /*========================================================================*/
  42. /**************************************************************************/
  43. /*!
  44. @brief Implements missing powf function
  45. */
  46. /**************************************************************************/
  47. float powf(const float x, const float y)
  48. {
  49. return (float)(pow((double)x, (double)y));
  50. }
  51. /**************************************************************************/
  52. /*!
  53. Enables the device
  54. Disables the device (putting it in lower power sleep mode)
  55. */
  56. /**************************************************************************/
  57. void APDS9960::enable(boolean en)
  58. {
  59. _enable.PON = en;
  60. this->write8(APDS9960_ENABLE, _enable.get());
  61. }
  62. /*========================================================================*/
  63. /* PUBLIC FUNCTIONS */
  64. /*========================================================================*/
  65. /**************************************************************************/
  66. /*!
  67. Initializes I2C and configures the sensor (call this function before
  68. doing anything else)
  69. */
  70. /**************************************************************************/
  71. boolean APDS9960::begin(uint16_t iTimeMS, apds9960AGain_t aGain, uint8_t addr)
  72. {
  73. _i2c_init();
  74. _i2caddr = addr;
  75. /* Make sure we're actually connected */
  76. uint8_t x = read8(APDS9960_ID);
  77. if (x != 0xAB)
  78. {
  79. return false;
  80. }
  81. /* Set default integration time and gain */
  82. setADCIntegrationTime(iTimeMS);
  83. setADCGain(aGain);
  84. // disable everything to start
  85. enableGesture(false);
  86. enableProximity(false);
  87. enableColor(false);
  88. disableColorInterrupt();
  89. disableProximityInterrupt();
  90. clearInterrupt();
  91. /* Note: by default, the device is in power down mode on bootup */
  92. enable(false);
  93. delay(10);
  94. enable(true);
  95. delay(10);
  96. //default to all gesture dimensions
  97. // setGestureDimensions(APDS9960_DIMENSIONS_ALL);
  98. // onion.io: changed to use only left and right
  99. // setGestureDimensions(APGS9960_DIMENSIONS_LEFT_RIGHT);
  100. // onion.io: changed to use only left and right. enable pcmp
  101. setGestureDimensions(APGS9960_DIMENSIONS_LEFT_RIGHT, 1);
  102. // onion.io:
  103. setGestureFIFOThreshold(APDS9960_GFIFO_4);
  104. // setGestureFIFOThreshold(APDS9960_GFIFO_16);
  105. setGestureGain(APDS9960_GGAIN_4);
  106. setGestureProximityThreshold(50);
  107. this->write8(APDS9960_GEXTH, 0); //onion.io remain in gesture mode indefinitely
  108. resetCounts();
  109. _gpulse.GPLEN = APDS9960_GPULSE_32US;
  110. _gpulse.GPULSE = 9; //10 pulses
  111. this->write8(APDS9960_GPULSE, _gpulse.get());
  112. return true;
  113. }
  114. /**************************************************************************/
  115. /*!
  116. Sets the integration time for the ADC of the APDS9960, in millis
  117. */
  118. /**************************************************************************/
  119. void APDS9960::setADCIntegrationTime(uint16_t iTimeMS)
  120. {
  121. float temp;
  122. // convert ms into 2.78ms increments
  123. temp = iTimeMS;
  124. temp /= 2.78;
  125. temp = 256 - temp;
  126. if (temp > 255) temp = 255;
  127. if (temp < 0) temp = 0;
  128. /* Update the timing register */
  129. write8(APDS9960_ATIME, (uint8_t)temp);
  130. }
  131. float APDS9960::getADCIntegrationTime(void)
  132. {
  133. float temp;
  134. temp = read8(APDS9960_ATIME);
  135. // convert to units of 2.78 ms
  136. temp = 256 - temp;
  137. temp *= 2.78;
  138. return temp;
  139. }
  140. /**************************************************************************/
  141. /*!
  142. Adjusts the color/ALS gain on the APDS9960 (adjusts the sensitivity to light)
  143. */
  144. /**************************************************************************/
  145. void APDS9960::setADCGain(apds9960AGain_t aGain)
  146. {
  147. _control.AGAIN = aGain;
  148. /* Update the timing register */
  149. write8(APDS9960_CONTROL, _control.get());
  150. }
  151. apds9960AGain_t APDS9960::getADCGain(void)
  152. {
  153. return (apds9960AGain_t) ( read8(APDS9960_CONTROL) & 0x03 );
  154. }
  155. /**************************************************************************/
  156. /*!
  157. Adjusts the Proximity gain on the APDS9960
  158. */
  159. /**************************************************************************/
  160. void APDS9960::setProxGain(apds9960PGain_t pGain)
  161. {
  162. _control.PGAIN = pGain;
  163. /* Update the timing register */
  164. write8(APDS9960_CONTROL, _control.get());
  165. }
  166. apds9960PGain_t APDS9960::getProxGain(void)
  167. {
  168. return (apds9960PGain_t) ( read8(APDS9960_CONTROL) & 0x0C );
  169. }
  170. void APDS9960::setProxPulse(apds9960PPulseLen_t pLen, uint8_t pulses) {
  171. if (pulses < 1) pulses = 1;
  172. if (pulses > 64) pulses = 64;
  173. pulses--;
  174. _ppulse.PPLEN = pLen;
  175. _ppulse.PPULSE = pulses;
  176. write8(APDS9960_PPULSE, _ppulse.get());
  177. }
  178. /**************************************************************************/
  179. /*!
  180. Enable proximity readings on APDS9960
  181. */
  182. /**************************************************************************/
  183. void APDS9960::enableProximity(boolean en)
  184. {
  185. _enable.PEN = en;
  186. write8(APDS9960_ENABLE, _enable.get());
  187. }
  188. void APDS9960::enableProximityInterrupt() {
  189. _enable.PIEN = 1;
  190. write8(APDS9960_ENABLE, _enable.get());
  191. clearInterrupt();
  192. }
  193. void APDS9960::disableProximityInterrupt() {
  194. _enable.PIEN = 0;
  195. write8(APDS9960_ENABLE, _enable.get());
  196. }
  197. void APDS9960::setProximityInterruptThreshold(uint8_t low, uint8_t high, uint8_t persistance){
  198. write8(APDS9960_PILT, low);
  199. write8(APDS9960_PIHT, high);
  200. if (persistance > 7) persistance = 7;
  201. _pers.PPERS = persistance;
  202. write8(APDS9960_PERS,_pers.get());
  203. }
  204. bool APDS9960::getProximityInterrupt()
  205. {
  206. _status.set(this->read8(APDS9960_STATUS));
  207. return _status.PINT;
  208. };
  209. /**************************************************************************/
  210. /*!
  211. Read proximity data
  212. */
  213. /**************************************************************************/
  214. uint8_t APDS9960::readProximity(void)
  215. {
  216. return read8(APDS9960_PDATA);
  217. }
  218. bool APDS9960::gestureValid()
  219. {
  220. _gstatus.set(this->read8(APDS9960_GSTATUS));
  221. return _gstatus.GVALID;
  222. }
  223. void APDS9960::setGestureDimensions(uint8_t dims, uint8_t pcmp_en)
  224. {
  225. _gconf3.GDIMS = dims;
  226. _gconf3.PCMP = pcmp_en;
  227. this->write8(APDS9960_GCONF3, _gconf3.get());
  228. }
  229. void APDS9960::setGestureFIFOThreshold(uint8_t thresh)
  230. {
  231. _gconf1.GFIFOTH = thresh;
  232. this->write8(APDS9960_GCONF1, _gconf1.get());
  233. }
  234. void APDS9960::setGestureGain(uint8_t gain)
  235. {
  236. _gconf2.GGAIN = gain;
  237. this->write8(APDS9960_GCONF2, _gconf2.get());
  238. }
  239. void APDS9960::setGestureProximityThreshold(uint8_t thresh)
  240. {
  241. this->write8(APDS9960_GPENTH, thresh);
  242. }
  243. void APDS9960::setGestureOffset(uint8_t offset_up, uint8_t offset_down, uint8_t offset_left, uint8_t offset_right)
  244. {
  245. this->write8(APDS9960_GOFFSET_U, offset_up);
  246. this->write8(APDS9960_GOFFSET_D, offset_down);
  247. this->write8(APDS9960_GOFFSET_L, offset_left);
  248. this->write8(APDS9960_GOFFSET_R, offset_right);
  249. }
  250. /**************************************************************************/
  251. /*!
  252. Enable gesture readings on APDS9960
  253. */
  254. /**************************************************************************/
  255. void APDS9960::enableGesture(boolean en)
  256. {
  257. if(!en){
  258. _gconf4.GMODE = 0;
  259. write8(APDS9960_GCONF4, _gconf4.get());
  260. }else{
  261. // onion.io force gmode, ignore proximity threshold
  262. _gconf4.GMODE = 1;
  263. write8(APDS9960_GCONF4, _gconf4.get());
  264. }
  265. _enable.GEN = en;
  266. write8(APDS9960_ENABLE, _enable.get());
  267. resetCounts();
  268. }
  269. void APDS9960::resetCounts()
  270. {
  271. gestCnt = 0;
  272. UCount = 0;
  273. DCount = 0;
  274. LCount = 0;
  275. RCount = 0;
  276. }
  277. uint8_t APDS9960::readGesture(void)
  278. {
  279. uint8_t toRead, bytesRead;
  280. uint8_t buf[256];
  281. unsigned long t;
  282. uint8_t gestureReceived;
  283. unsigned long previousMillis = millis();
  284. while(1){
  285. if (millis() - previousMillis >= 250) {
  286. //Gesture tiemout, took too long to read
  287. return 0;
  288. }
  289. int up_down_diff = 0;
  290. int left_right_diff = 0;
  291. gestureReceived = 0;
  292. if(!gestureValid()) return 0;
  293. delay(30);
  294. toRead = this->read8(APDS9960_GFLVL);
  295. bytesRead = this->read(APDS9960_GFIFO_U, buf, toRead);
  296. if(abs((int)buf[0] - (int)buf[1]) > 13)
  297. up_down_diff += (int)buf[0] - (int)buf[1];
  298. if(abs((int)buf[2] - (int)buf[3]) > 13)
  299. left_right_diff += (int)buf[2] - (int)buf[3];
  300. if(up_down_diff != 0){
  301. if(up_down_diff < 0){
  302. if( DCount > 0){
  303. gestureReceived = APDS9960_UP;
  304. }
  305. else UCount++;
  306. }
  307. else if(up_down_diff > 0){
  308. if( UCount > 0){
  309. gestureReceived = APDS9960_DOWN;
  310. }
  311. else DCount++;
  312. }
  313. }
  314. if(left_right_diff != 0){
  315. if(left_right_diff < 0){
  316. if( RCount > 0){
  317. gestureReceived = APDS9960_LEFT;
  318. }
  319. else LCount++;
  320. }
  321. else if(left_right_diff > 0){
  322. if( LCount > 0){
  323. gestureReceived = APDS9960_RIGHT;
  324. }
  325. else RCount++;
  326. }
  327. }
  328. if(up_down_diff != 0 || left_right_diff != 0) t = millis();
  329. if(gestureReceived || millis() - t > 300){
  330. resetCounts();
  331. return gestureReceived;
  332. }
  333. }
  334. }
  335. /**************************************************************************/
  336. /*!
  337. Set LED brightness for proximity/gesture
  338. */
  339. /**************************************************************************/
  340. void APDS9960::setLED(apds9960LedDrive_t drive, apds9960LedBoost_t boost) {
  341. // set BOOST
  342. _config2.LED_BOOST = boost;
  343. write8(APDS9960_CONFIG2, _config2.get());
  344. _control.LDRIVE = drive;
  345. write8(APDS9960_CONTROL, _control.get());
  346. }
  347. /**************************************************************************/
  348. /*!
  349. Enable proximity readings on APDS9960
  350. */
  351. /**************************************************************************/
  352. void APDS9960::enableColor(boolean en)
  353. {
  354. _enable.AEN = en;
  355. write8(APDS9960_ENABLE, _enable.get());
  356. }
  357. bool APDS9960::colorDataReady()
  358. {
  359. _status.set(this->read8(APDS9960_STATUS));
  360. return _status.AVALID;
  361. }
  362. /**************************************************************************/
  363. /*!
  364. @brief Reads the raw red, green, blue and clear channel values
  365. */
  366. /**************************************************************************/
  367. void APDS9960::getColorData (uint16_t *r, uint16_t *g, uint16_t *b, uint16_t *c)
  368. {
  369. *c = read16R(APDS9960_CDATAL);
  370. *r = read16R(APDS9960_RDATAL);
  371. *g = read16R(APDS9960_GDATAL);
  372. *b = read16R(APDS9960_BDATAL);
  373. }
  374. /**************************************************************************/
  375. /*!
  376. @brief Converts the raw R/G/B values to color temperature in degrees
  377. Kelvin
  378. */
  379. /**************************************************************************/
  380. uint16_t APDS9960::calculateColorTemperature(uint16_t r, uint16_t g, uint16_t b)
  381. {
  382. float X, Y, Z; /* RGB to XYZ correlation */
  383. float xc, yc; /* Chromaticity co-ordinates */
  384. float n; /* McCamy's formula */
  385. float cct;
  386. /* 1. Map RGB values to their XYZ counterparts. */
  387. /* Based on 6500K fluorescent, 3000K fluorescent */
  388. /* and 60W incandescent values for a wide range. */
  389. /* Note: Y = Illuminance or lux */
  390. X = (-0.14282F * r) + (1.54924F * g) + (-0.95641F * b);
  391. Y = (-0.32466F * r) + (1.57837F * g) + (-0.73191F * b);
  392. Z = (-0.68202F * r) + (0.77073F * g) + ( 0.56332F * b);
  393. /* 2. Calculate the chromaticity co-ordinates */
  394. xc = (X) / (X + Y + Z);
  395. yc = (Y) / (X + Y + Z);
  396. /* 3. Use McCamy's formula to determine the CCT */
  397. n = (xc - 0.3320F) / (0.1858F - yc);
  398. /* Calculate the final CCT */
  399. cct = (449.0F * powf(n, 3)) + (3525.0F * powf(n, 2)) + (6823.3F * n) + 5520.33F;
  400. /* Return the results in degrees Kelvin */
  401. return (uint16_t)cct;
  402. }
  403. /**************************************************************************/
  404. /*!
  405. @brief Calculate ambient light values
  406. */
  407. /**************************************************************************/
  408. uint16_t APDS9960::calculateLux(uint16_t r, uint16_t g, uint16_t b)
  409. {
  410. float illuminance;
  411. /* This only uses RGB ... how can we integrate clear or calculate lux */
  412. /* based exclusively on clear since this might be more reliable? */
  413. illuminance = (-0.32466F * r) + (1.57837F * g) + (-0.73191F * b);
  414. return (uint16_t)illuminance;
  415. }
  416. void APDS9960::enableColorInterrupt() {
  417. _enable.AIEN = 1;
  418. write8(APDS9960_ENABLE, _enable.get());
  419. }
  420. void APDS9960::disableColorInterrupt() {
  421. _enable.AIEN = 0;
  422. write8(APDS9960_ENABLE, _enable.get());
  423. }
  424. void APDS9960::clearInterrupt(void) {
  425. this->write(APDS9960_AICLEAR, NULL, 0);
  426. }
  427. void APDS9960::setIntLimits(uint16_t low, uint16_t high) {
  428. write8(APDS9960_AILTIL, low & 0xFF);
  429. write8(APDS9960_AILTH, low >> 8);
  430. write8(APDS9960_AIHTL, high & 0xFF);
  431. write8(APDS9960_AIHTH, high >> 8);
  432. }
  433. void APDS9960::write8(byte reg, byte value)
  434. {
  435. this->write(reg, &value, 1);
  436. }
  437. uint8_t APDS9960::read8(byte reg)
  438. {
  439. uint8_t ret;
  440. this->read(reg, &ret, 1);
  441. return ret;
  442. }
  443. uint32_t APDS9960::read32(uint8_t reg)
  444. {
  445. uint8_t ret[4];
  446. this->read(reg, ret, 4);
  447. return (ret[0] << 24) | (ret[1] << 16) | (ret[2] << 8) | ret[3];
  448. }
  449. uint16_t APDS9960::read16(uint8_t reg)
  450. {
  451. uint8_t ret[2];
  452. this->read(reg, ret, 2);
  453. return (ret[0] << 8) | ret[1];
  454. }
  455. uint16_t APDS9960::read16R(uint8_t reg)
  456. {
  457. uint8_t ret[2];
  458. this->read(reg, ret, 2);
  459. return (ret[1] << 8) | ret[0];
  460. }
  461. void APDS9960::_i2c_init()
  462. {
  463. Wire.begin();
  464. }
  465. uint8_t APDS9960::read(uint8_t reg, uint8_t *buf, uint8_t num)
  466. {
  467. uint8_t value;
  468. uint8_t pos = 0;
  469. bool eof = false;
  470. //on arduino we need to read in 32 byte chunks
  471. while(pos < num && !eof){
  472. uint8_t read_now = min(32, num - pos);
  473. Wire.beginTransmission((uint8_t)_i2caddr);
  474. Wire.write((uint8_t)reg + pos);
  475. Wire.endTransmission();
  476. Wire.requestFrom((uint8_t)_i2caddr, read_now);
  477. for(int i=0; i<read_now; i++){
  478. if(!Wire.available()){
  479. eof = true;
  480. break;
  481. }
  482. buf[pos] = Wire.read();
  483. pos++;
  484. }
  485. }
  486. return pos;
  487. }
  488. void APDS9960::write(uint8_t reg, uint8_t *buf, uint8_t num)
  489. {
  490. Wire.beginTransmission((uint8_t)_i2caddr);
  491. Wire.write((uint8_t)reg);
  492. Wire.write((uint8_t *)buf, num);
  493. Wire.endTransmission();
  494. }