| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073 |
- /*
- xsns_27_apds9960.ino - Support for I2C APDS9960 Proximity Sensor for Sonoff-Tasmota
- Copyright (C) 2018 Shawn Hymel/Sparkfun and Theo Arends
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
- - Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- - Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- POSSIBILITY OF SUCH DAMAGE.
- */
- #ifdef USE_I2C
- #ifdef USE_APDS9960
- /*********************************************************************************************\
- * APDS9960 - Digital Proximity Ambient Light RGB and Gesture Sensor
- *
- * Source: Shawn Hymel (SparkFun Electronics)
- * Adaption for TASMOTA: Christian Baars
- *
- * I2C Address: 0x39
- \*********************************************************************************************/
- #define XSNS_27 27
- #if defined(USE_SHT) || defined(USE_VEML6070) || defined(USE_TSL2561)
- #warning **** Turned off conflicting drivers SHT and VEML6070 ****
- #ifdef USE_SHT
- #undef USE_SHT // SHT-Driver blocks gesture sensor
- #endif
- #ifdef USE_VEML6070
- #undef USE_VEML6070 // address conflict on the I2C-bus
- #endif
- #ifdef USE_TSL2561
- #undef USE_TSL2561 // possible address conflict on the I2C-bus
- #endif
- #endif
- #define APDS9960_I2C_ADDR 0x39
- #define APDS9960_CHIPID_1 0xAB
- #define APDS9960_CHIPID_2 0x9C
- #define APDS9930_CHIPID_1 0x12 // we will check, if someone got an incorrect sensor
- #define APDS9930_CHIPID_2 0x39 // there are case reports about "accidentially bought" 9930's
- /* Gesture parameters */
- #define GESTURE_THRESHOLD_OUT 10
- #define GESTURE_SENSITIVITY_1 50
- #define GESTURE_SENSITIVITY_2 20
- uint8_t APDS9960addr;
- uint8_t APDS9960type = 0;
- char APDS9960stype[9];
- char currentGesture[6];
- uint8_t gesture_mode = 1;
- volatile uint8_t recovery_loop_counter = 0; //count number of stateloops to switch the sensor off, if needed
- #define APDS9960_LONG_RECOVERY 50 //long pause after sensor overload in loops
- #define APDS9960_MAX_GESTURE_CYCLES 50 //how many FIFO-reads are allowed to prevent crash
- bool APDS9960_overload = false;
- #ifdef USE_WEBSERVER
- const char HTTP_APDS_9960_SNS[] PROGMEM = "%s"
- "{s}" "Red" "{m}%s{e}"
- "{s}" "Green" "{m}%s{e}"
- "{s}" "Blue" "{m}%s{e}"
- "{s}" "Ambient" "{m}%s " D_UNIT_LUX "{e}"
- "{s}" "CCT" "{m}%s " "K" "{e}" // calculated color temperature in Kelvin
- "{s}" "Proximity" "{m}%s{e}"; // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
- #endif // USE_WEBSERVER
- /*********************************************************************************************\
- * APDS9960
- *
- * Programmer : APDS9960 Datasheet and Sparkfun
- \*********************************************************************************************/
- /* Misc parameters */
- #define FIFO_PAUSE_TIME 30 // Wait period (ms) between FIFO reads
- /* APDS-9960 register addresses */
- #define APDS9960_ENABLE 0x80
- #define APDS9960_ATIME 0x81
- #define APDS9960_WTIME 0x83
- #define APDS9960_AILTL 0x84
- #define APDS9960_AILTH 0x85
- #define APDS9960_AIHTL 0x86
- #define APDS9960_AIHTH 0x87
- #define APDS9960_PILT 0x89
- #define APDS9960_PIHT 0x8B
- #define APDS9960_PERS 0x8C
- #define APDS9960_CONFIG1 0x8D
- #define APDS9960_PPULSE 0x8E
- #define APDS9960_CONTROL 0x8F
- #define APDS9960_CONFIG2 0x90
- #define APDS9960_ID 0x92
- #define APDS9960_STATUS 0x93
- #define APDS9960_CDATAL 0x94
- #define APDS9960_CDATAH 0x95
- #define APDS9960_RDATAL 0x96
- #define APDS9960_RDATAH 0x97
- #define APDS9960_GDATAL 0x98
- #define APDS9960_GDATAH 0x99
- #define APDS9960_BDATAL 0x9A
- #define APDS9960_BDATAH 0x9B
- #define APDS9960_PDATA 0x9C
- #define APDS9960_POFFSET_UR 0x9D
- #define APDS9960_POFFSET_DL 0x9E
- #define APDS9960_CONFIG3 0x9F
- #define APDS9960_GPENTH 0xA0
- #define APDS9960_GEXTH 0xA1
- #define APDS9960_GCONF1 0xA2
- #define APDS9960_GCONF2 0xA3
- #define APDS9960_GOFFSET_U 0xA4
- #define APDS9960_GOFFSET_D 0xA5
- #define APDS9960_GOFFSET_L 0xA7
- #define APDS9960_GOFFSET_R 0xA9
- #define APDS9960_GPULSE 0xA6
- #define APDS9960_GCONF3 0xAA
- #define APDS9960_GCONF4 0xAB
- #define APDS9960_GFLVL 0xAE
- #define APDS9960_GSTATUS 0xAF
- #define APDS9960_IFORCE 0xE4
- #define APDS9960_PICLEAR 0xE5
- #define APDS9960_CICLEAR 0xE6
- #define APDS9960_AICLEAR 0xE7
- #define APDS9960_GFIFO_U 0xFC
- #define APDS9960_GFIFO_D 0xFD
- #define APDS9960_GFIFO_L 0xFE
- #define APDS9960_GFIFO_R 0xFF
- /* Bit fields */
- #define APDS9960_PON 0b00000001
- #define APDS9960_AEN 0b00000010
- #define APDS9960_PEN 0b00000100
- #define APDS9960_WEN 0b00001000
- #define APSD9960_AIEN 0b00010000
- #define APDS9960_PIEN 0b00100000
- #define APDS9960_GEN 0b01000000
- #define APDS9960_GVALID 0b00000001
- /* On/Off definitions */
- #define OFF 0
- #define ON 1
- /* Acceptable parameters for setMode */
- #define POWER 0
- #define AMBIENT_LIGHT 1
- #define PROXIMITY 2
- #define WAIT 3
- #define AMBIENT_LIGHT_INT 4
- #define PROXIMITY_INT 5
- #define GESTURE 6
- #define ALL 7
- /* LED Drive values */
- #define LED_DRIVE_100MA 0
- #define LED_DRIVE_50MA 1
- #define LED_DRIVE_25MA 2
- #define LED_DRIVE_12_5MA 3
- /* Proximity Gain (PGAIN) values */
- #define PGAIN_1X 0
- #define PGAIN_2X 1
- #define PGAIN_4X 2
- #define PGAIN_8X 3
- /* ALS Gain (AGAIN) values */
- #define AGAIN_1X 0
- #define AGAIN_4X 1
- #define AGAIN_16X 2
- #define AGAIN_64X 3
- /* Gesture Gain (GGAIN) values */
- #define GGAIN_1X 0
- #define GGAIN_2X 1
- #define GGAIN_4X 2
- #define GGAIN_8X 3
- /* LED Boost values */
- #define LED_BOOST_100 0
- #define LED_BOOST_150 1
- #define LED_BOOST_200 2
- #define LED_BOOST_300 3
- /* Gesture wait time values */
- #define GWTIME_0MS 0
- #define GWTIME_2_8MS 1
- #define GWTIME_5_6MS 2
- #define GWTIME_8_4MS 3
- #define GWTIME_14_0MS 4
- #define GWTIME_22_4MS 5
- #define GWTIME_30_8MS 6
- #define GWTIME_39_2MS 7
- /* Default values */
- #define DEFAULT_ATIME 0xdb // 103ms = 0xdb
- #define DEFAULT_WTIME 246 // 27ms
- #define DEFAULT_PROX_PPULSE 0x87 // 16us, 8 pulses
- #define DEFAULT_GESTURE_PPULSE 0x89 // 16us, 10 pulses ---89
- #define DEFAULT_POFFSET_UR 0 // 0 offset
- #define DEFAULT_POFFSET_DL 0 // 0 offset
- #define DEFAULT_CONFIG1 0x60 // No 12x wait (WTIME) factor
- #define DEFAULT_LDRIVE LED_DRIVE_100MA
- #define DEFAULT_PGAIN PGAIN_4X
- #define DEFAULT_AGAIN AGAIN_4X // we have to divide by the same facot at the end
- #define DEFAULT_PILT 0 // Low proximity threshold
- #define DEFAULT_PIHT 50 // High proximity threshold
- #define DEFAULT_AILT 0xFFFF // Force interrupt for calibration
- #define DEFAULT_AIHT 0
- #define DEFAULT_PERS 0x11 // 2 consecutive prox or ALS for int.
- #define DEFAULT_CONFIG2 0x01 // No saturation interrupts or LED boost
- #define DEFAULT_CONFIG3 0 // Enable all photodiodes, no SAI
- #define DEFAULT_GPENTH 40 // Threshold for entering gesture mode
- #define DEFAULT_GEXTH 30 // Threshold for exiting gesture mode
- #define DEFAULT_GCONF1 0x40 // 4 gesture events for int., 1 for exit
- #define DEFAULT_GGAIN GGAIN_4X
- #define DEFAULT_GLDRIVE LED_DRIVE_100MA // default 100ma
- #define DEFAULT_GWTIME GWTIME_2_8MS // default 2_8MS
- #define DEFAULT_GOFFSET 0 // No offset scaling for gesture mode
- #define DEFAULT_GPULSE 0xC9 // 32us, 10 pulses
- #define DEFAULT_GCONF3 0 // All photodiodes active during gesture
- #define DEFAULT_GIEN 0 // Disable gesture interrupts
- #define ERROR 0xFF
- /* Direction definitions */
- enum {
- DIR_NONE,
- DIR_LEFT,
- DIR_RIGHT,
- DIR_UP,
- DIR_DOWN,
- DIR_ALL
- };
- /* State definitions*/
- enum {
- APDS9960_NA_STATE,
- APDS9960_ALL_STATE
- };
- /* Container for gesture data */
- typedef struct gesture_data_type {
- uint8_t u_data[32];
- uint8_t d_data[32];
- uint8_t l_data[32];
- uint8_t r_data[32];
- uint8_t index;
- uint8_t total_gestures;
- uint8_t in_threshold;
- uint8_t out_threshold;
- } gesture_data_type;
- /*Members*/
- gesture_data_type gesture_data_;
- int16_t gesture_ud_delta_ = 0;
- int16_t gesture_lr_delta_ = 0;
- int16_t gesture_ud_count_ = 0;
- int16_t gesture_lr_count_ = 0;
- int16_t gesture_state_ = 0;
- int16_t gesture_motion_ = DIR_NONE;
- typedef struct color_data_type {
- uint16_t a; // measured ambient
- uint16_t r;
- uint16_t g;
- uint16_t b;
- uint8_t p; // proximity
- uint16_t cct; // calculated color temperature
- uint16_t lux; // calculated illuminance - atm only from rgb
- } color_data_type;
- color_data_type color_data;
- uint8_t APDS9960_aTime = DEFAULT_ATIME;
- /*******************************************************************************
- * Helper functions
- ******************************************************************************/
- /**
- * @brief Writes a single byte to the I2C device (no register)
- *
- * @param[in] val the 1-byte value to write to the I2C device
- * @return True if successful write operation. False otherwise.
- */
- bool wireWriteByte(uint8_t val)
- {
- Wire.beginTransmission(APDS9960_I2C_ADDR);
- Wire.write(val);
- if( Wire.endTransmission() != 0 ) {
- return false;
- }
- return true;
- }
- /**
- * @brief Reads a block (array) of bytes from the I2C device and register
- *
- * @param[in] reg the register to read from
- * @param[out] val pointer to the beginning of the data
- * @param[in] len number of bytes to read
- * @return Number of bytes read. -1 on read error.
- */
- int8_t wireReadDataBlock( uint8_t reg,
- uint8_t *val,
- uint16_t len)
- {
- unsigned char i = 0;
- /* Indicate which register we want to read from */
- if (!wireWriteByte(reg)) {
- return -1;
- }
- /* Read block data */
- Wire.requestFrom(APDS9960_I2C_ADDR, len);
- while (Wire.available()) {
- if (i >= len) {
- return -1;
- }
- val[i] = Wire.read();
- i++;
- }
- return i;
- }
- /**
- * Taken from the Adafruit-library
- * @brief Converts the raw R/G/B values to color temperature in degrees
- * Kelvin
- */
- void calculateColorTemperature(void)
- {
- float X, Y, Z; /* RGB to XYZ correlation */
- float xc, yc; /* Chromaticity co-ordinates */
- float n; /* McCamy's formula */
- float cct;
- /* 1. Map RGB values to their XYZ counterparts. */
- /* Based on 6500K fluorescent, 3000K fluorescent */
- /* and 60W incandescent values for a wide range. */
- /* Note: Y = Illuminance or lux */
- X = (-0.14282F * color_data.r) + (1.54924F * color_data.g) + (-0.95641F * color_data.b);
- Y = (-0.32466F * color_data.r) + (1.57837F * color_data.g) + (-0.73191F * color_data.b); // this is Lux ... under certain circumstances
- Z = (-0.68202F * color_data.r) + (0.77073F * color_data.g) + ( 0.56332F * color_data.b);
- /* 2. Calculate the chromaticity co-ordinates */
- xc = (X) / (X + Y + Z);
- yc = (Y) / (X + Y + Z);
- /* 3. Use McCamy's formula to determine the CCT */
- n = (xc - 0.3320F) / (0.1858F - yc);
- /* Calculate the final CCT */
- color_data.cct = (449.0F * powf(n, 3)) + (3525.0F * powf(n, 2)) + (6823.3F * n) + 5520.33F;
- return;
- }
- /**
- * Taken from the Adafruit-Library
- * @brief Implements missing powf function
- */
- float powf(const float x, const float y)
- {
- return (float)(pow((double)x, (double)y));
- }
- /*******************************************************************************
- * Getters and setters for register values
- ******************************************************************************/
- /**
- * @brief Returns the lower threshold for proximity detection
- *
- * @return lower threshold
- */
- uint8_t getProxIntLowThresh(void)
- {
- uint8_t val;
- /* Read value from PILT register */
- val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PILT) ;
- return val;
- }
- /**
- * @brief Sets the lower threshold for proximity detection
- *
- * @param[in] threshold the lower proximity threshold
- */
- void setProxIntLowThresh(uint8_t threshold)
- {
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PILT, threshold);
- }
- /**
- * @brief Returns the high threshold for proximity detection
- *
- * @return high threshold
- */
- uint8_t getProxIntHighThresh(void)
- {
- uint8_t val;
- /* Read value from PIHT register */
- val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PIHT) ;
- return val;
- }
- /**
- * @brief Sets the high threshold for proximity detection
- *
- * @param[in] threshold the high proximity threshold
- */
- void setProxIntHighThresh(uint8_t threshold)
- {
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PIHT, threshold);
- }
- /**
- * @brief Returns LED drive strength for proximity and ALS
- *
- * Value LED Current
- * 0 100 mA
- * 1 50 mA
- * 2 25 mA
- * 3 12.5 mA
- *
- * @return the value of the LED drive strength. 0xFF on failure.
- */
- uint8_t getLEDDrive(void)
- {
- uint8_t val;
- /* Read value from CONTROL register */
- val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL) ;
- /* Shift and mask out LED drive bits */
- val = (val >> 6) & 0b00000011;
- return val;
- }
- /**
- * @brief Sets the LED drive strength for proximity and ALS
- *
- * Value LED Current
- * 0 100 mA
- * 1 50 mA
- * 2 25 mA
- * 3 12.5 mA
- *
- * @param[in] drive the value (0-3) for the LED drive strength
- */
- void setLEDDrive(uint8_t drive)
- {
- uint8_t val;
- /* Read value from CONTROL register */
- val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL);
- /* Set bits in register to given value */
- drive &= 0b00000011;
- drive = drive << 6;
- val &= 0b00111111;
- val |= drive;
- /* Write register value back into CONTROL register */
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONTROL, val);
- }
- /**
- * @brief Returns receiver gain for proximity detection
- *
- * Value Gain
- * 0 1x
- * 1 2x
- * 2 4x
- * 3 8x
- *
- * @return the value of the proximity gain. 0xFF on failure.
- */
- uint8_t getProximityGain(void)
- {
- uint8_t val;
- /* Read value from CONTROL register */
- val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL) ;
- /* Shift and mask out PDRIVE bits */
- val = (val >> 2) & 0b00000011;
- return val;
- }
- /**
- * @brief Sets the receiver gain for proximity detection
- *
- * Value Gain
- * 0 1x
- * 1 2x
- * 2 4x
- * 3 8x
- *
- * @param[in] drive the value (0-3) for the gain
- */
- void setProximityGain(uint8_t drive)
- {
- uint8_t val;
- /* Read value from CONTROL register */
- val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL);
- /* Set bits in register to given value */
- drive &= 0b00000011;
- drive = drive << 2;
- val &= 0b11110011;
- val |= drive;
- /* Write register value back into CONTROL register */
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONTROL, val);
- }
- /**
- * @brief Returns receiver gain for the ambient light sensor (ALS)
- *
- * Value Gain
- * 0 1x
- * 1 4x
- * 2 16x
- * 3 64x
- *
- * @return the value of the ALS gain. 0xFF on failure.
- */
- /**
- * @brief Sets the receiver gain for the ambient light sensor (ALS)
- *
- * Value Gain
- * 0 1x
- * 1 4x
- * 2 16x
- * 3 64x
- *
- * @param[in] drive the value (0-3) for the gain
- */
- void setAmbientLightGain(uint8_t drive)
- {
- uint8_t val;
- /* Read value from CONTROL register */
- val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL);
- /* Set bits in register to given value */
- drive &= 0b00000011;
- val &= 0b11111100;
- val |= drive;
- /* Write register value back into CONTROL register */
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONTROL, val);
- }
- /**
- * @brief Get the current LED boost value
- *
- * Value Boost Current
- * 0 100%
- * 1 150%
- * 2 200%
- * 3 300%
- *
- * @return The LED boost value. 0xFF on failure.
- */
- uint8_t getLEDBoost(void)
- {
- uint8_t val;
- /* Read value from CONFIG2 register */
- val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG2) ;
- /* Shift and mask out LED_BOOST bits */
- val = (val >> 4) & 0b00000011;
- return val;
- }
- /**
- * @brief Sets the LED current boost value
- *
- * Value Boost Current
- * 0 100%
- * 1 150%
- * 2 200%
- * 3 300%
- *
- * @param[in] drive the value (0-3) for current boost (100-300%)
- */
- void setLEDBoost(uint8_t boost)
- {
- uint8_t val;
- /* Read value from CONFIG2 register */
- val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG2) ;
- /* Set bits in register to given value */
- boost &= 0b00000011;
- boost = boost << 4;
- val &= 0b11001111;
- val |= boost;
- /* Write register value back into CONFIG2 register */
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG2, val) ;
- }
- /**
- * @brief Gets proximity gain compensation enable
- *
- * @return 1 if compensation is enabled. 0 if not. 0xFF on error.
- */
- uint8_t getProxGainCompEnable(void)
- {
- uint8_t val;
- /* Read value from CONFIG3 register */
- val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG3) ;
- /* Shift and mask out PCMP bits */
- val = (val >> 5) & 0b00000001;
- return val;
- }
- /**
- * @brief Sets the proximity gain compensation enable
- *
- * @param[in] enable 1 to enable compensation. 0 to disable compensation.
- */
- void setProxGainCompEnable(uint8_t enable)
- {
- uint8_t val;
- /* Read value from CONFIG3 register */
- val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG3) ;
- /* Set bits in register to given value */
- enable &= 0b00000001;
- enable = enable << 5;
- val &= 0b11011111;
- val |= enable;
- /* Write register value back into CONFIG3 register */
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG3, val) ;
- }
- /**
- * @brief Gets the current mask for enabled/disabled proximity photodiodes
- *
- * 1 = disabled, 0 = enabled
- * Bit Photodiode
- * 3 UP
- * 2 DOWN
- * 1 LEFT
- * 0 RIGHT
- *
- * @return Current proximity mask for photodiodes. 0xFF on error.
- */
- uint8_t getProxPhotoMask(void)
- {
- uint8_t val;
- /* Read value from CONFIG3 register */
- val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG3) ;
- /* Mask out photodiode enable mask bits */
- val &= 0b00001111;
- return val;
- }
- /**
- * @brief Sets the mask for enabling/disabling proximity photodiodes
- *
- * 1 = disabled, 0 = enabled
- * Bit Photodiode
- * 3 UP
- * 2 DOWN
- * 1 LEFT
- * 0 RIGHT
- *
- * @param[in] mask 4-bit mask value
- */
- void setProxPhotoMask(uint8_t mask)
- {
- uint8_t val;
- /* Read value from CONFIG3 register */
- val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG3) ;
- /* Set bits in register to given value */
- mask &= 0b00001111;
- val &= 0b11110000;
- val |= mask;
- /* Write register value back into CONFIG3 register */
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG3, val) ;
- }
- /**
- * @brief Gets the entry proximity threshold for gesture sensing
- *
- * @return Current entry proximity threshold.
- */
- uint8_t getGestureEnterThresh(void)
- {
- uint8_t val;
- /* Read value from GPENTH register */
- val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GPENTH) ;
- return val;
- }
- /**
- * @brief Sets the entry proximity threshold for gesture sensing
- *
- * @param[in] threshold proximity value needed to start gesture mode
- */
- void setGestureEnterThresh(uint8_t threshold)
- {
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GPENTH, threshold) ;
- }
- /**
- * @brief Gets the exit proximity threshold for gesture sensing
- *
- * @return Current exit proximity threshold.
- */
- uint8_t getGestureExitThresh(void)
- {
- uint8_t val;
- /* Read value from GEXTH register */
- val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GEXTH) ;
- return val;
- }
- /**
- * @brief Sets the exit proximity threshold for gesture sensing
- *
- * @param[in] threshold proximity value needed to end gesture mode
- */
- void setGestureExitThresh(uint8_t threshold)
- {
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GEXTH, threshold) ;
- }
- /**
- * @brief Gets the gain of the photodiode during gesture mode
- *
- * Value Gain
- * 0 1x
- * 1 2x
- * 2 4x
- * 3 8x
- *
- * @return the current photodiode gain. 0xFF on error.
- */
- uint8_t getGestureGain(void)
- {
- uint8_t val;
- /* Read value from GCONF2 register */
- val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ;
- /* Shift and mask out GGAIN bits */
- val = (val >> 5) & 0b00000011;
- return val;
- }
- /**
- * @brief Sets the gain of the photodiode during gesture mode
- *
- * Value Gain
- * 0 1x
- * 1 2x
- * 2 4x
- * 3 8x
- *
- * @param[in] gain the value for the photodiode gain
- */
- void setGestureGain(uint8_t gain)
- {
- uint8_t val;
- /* Read value from GCONF2 register */
- val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ;
- /* Set bits in register to given value */
- gain &= 0b00000011;
- gain = gain << 5;
- val &= 0b10011111;
- val |= gain;
- /* Write register value back into GCONF2 register */
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF2, val) ;
- }
- /**
- * @brief Gets the drive current of the LED during gesture mode
- *
- * Value LED Current
- * 0 100 mA
- * 1 50 mA
- * 2 25 mA
- * 3 12.5 mA
- *
- * @return the LED drive current value. 0xFF on error.
- */
- uint8_t getGestureLEDDrive(void)
- {
- uint8_t val;
- /* Read value from GCONF2 register */
- val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ;
- /* Shift and mask out GLDRIVE bits */
- val = (val >> 3) & 0b00000011;
- return val;
- }
- /**
- * @brief Sets the LED drive current during gesture mode
- *
- * Value LED Current
- * 0 100 mA
- * 1 50 mA
- * 2 25 mA
- * 3 12.5 mA
- *
- * @param[in] drive the value for the LED drive current
- */
- void setGestureLEDDrive(uint8_t drive)
- {
- uint8_t val;
- /* Read value from GCONF2 register */
- val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ;
- /* Set bits in register to given value */
- drive &= 0b00000011;
- drive = drive << 3;
- val &= 0b11100111;
- val |= drive;
- /* Write register value back into GCONF2 register */
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF2, val) ;
- }
- /**
- * @brief Gets the time in low power mode between gesture detections
- *
- * Value Wait time
- * 0 0 ms
- * 1 2.8 ms
- * 2 5.6 ms
- * 3 8.4 ms
- * 4 14.0 ms
- * 5 22.4 ms
- * 6 30.8 ms
- * 7 39.2 ms
- *
- * @return the current wait time between gestures. 0xFF on error.
- */
- uint8_t getGestureWaitTime(void)
- {
- uint8_t val;
- /* Read value from GCONF2 register */
- val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ;
- /* Mask out GWTIME bits */
- val &= 0b00000111;
- return val;
- }
- /**
- * @brief Sets the time in low power mode between gesture detections
- *
- * Value Wait time
- * 0 0 ms
- * 1 2.8 ms
- * 2 5.6 ms
- * 3 8.4 ms
- * 4 14.0 ms
- * 5 22.4 ms
- * 6 30.8 ms
- * 7 39.2 ms
- *
- * @param[in] the value for the wait time
- */
- void setGestureWaitTime(uint8_t time)
- {
- uint8_t val;
- /* Read value from GCONF2 register */
- val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ;
- /* Set bits in register to given value */
- time &= 0b00000111;
- val &= 0b11111000;
- val |= time;
- /* Write register value back into GCONF2 register */
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF2, val) ;
- }
- /**
- * @brief Gets the low threshold for ambient light interrupts
- *
- * @param[out] threshold current low threshold stored on the APDS-9960
- */
- void getLightIntLowThreshold(uint16_t &threshold)
- {
- uint8_t val_byte;
- threshold = 0;
- /* Read value from ambient light low threshold, low byte register */
- val_byte = I2cRead8(APDS9960_I2C_ADDR, APDS9960_AILTL) ;
- threshold = val_byte;
- /* Read value from ambient light low threshold, high byte register */
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AILTH, val_byte) ;
- threshold = threshold + ((uint16_t)val_byte << 8);
- }
- /**
- * @brief Sets the low threshold for ambient light interrupts
- *
- * @param[in] threshold low threshold value for interrupt to trigger
- */
- void setLightIntLowThreshold(uint16_t threshold)
- {
- uint8_t val_low;
- uint8_t val_high;
- /* Break 16-bit threshold into 2 8-bit values */
- val_low = threshold & 0x00FF;
- val_high = (threshold & 0xFF00) >> 8;
- /* Write low byte */
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AILTL, val_low) ;
- /* Write high byte */
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AILTH, val_high) ;
- }
- /**
- * @brief Gets the high threshold for ambient light interrupts
- *
- * @param[out] threshold current low threshold stored on the APDS-9960
- */
- void getLightIntHighThreshold(uint16_t &threshold)
- {
- uint8_t val_byte;
- threshold = 0;
- /* Read value from ambient light high threshold, low byte register */
- val_byte = I2cRead8(APDS9960_I2C_ADDR, APDS9960_AIHTL);
- threshold = val_byte;
- /* Read value from ambient light high threshold, high byte register */
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AIHTH, val_byte) ;
- threshold = threshold + ((uint16_t)val_byte << 8);
- }
- /**
- * @brief Sets the high threshold for ambient light interrupts
- *
- * @param[in] threshold high threshold value for interrupt to trigger
- */
- void setLightIntHighThreshold(uint16_t threshold)
- {
- uint8_t val_low;
- uint8_t val_high;
- /* Break 16-bit threshold into 2 8-bit values */
- val_low = threshold & 0x00FF;
- val_high = (threshold & 0xFF00) >> 8;
- /* Write low byte */
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AIHTL, val_low);
- /* Write high byte */
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AIHTH, val_high) ;
- }
- /**
- * @brief Gets the low threshold for proximity interrupts
- *
- * @param[out] threshold current low threshold stored on the APDS-9960
- */
- void getProximityIntLowThreshold(uint8_t &threshold)
- {
- threshold = 0;
- /* Read value from proximity low threshold register */
- threshold = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PILT);
- }
- /**
- * @brief Sets the low threshold for proximity interrupts
- *
- * @param[in] threshold low threshold value for interrupt to trigger
- */
- void setProximityIntLowThreshold(uint8_t threshold)
- {
- /* Write threshold value to register */
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PILT, threshold) ;
- }
- /**
- * @brief Gets the high threshold for proximity interrupts
- *
- * @param[out] threshold current low threshold stored on the APDS-9960
- */
- void getProximityIntHighThreshold(uint8_t &threshold)
- {
- threshold = 0;
- /* Read value from proximity low threshold register */
- threshold = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PIHT) ;
- }
- /**
- * @brief Sets the high threshold for proximity interrupts
- *
- * @param[in] threshold high threshold value for interrupt to trigger
- */
- void setProximityIntHighThreshold(uint8_t threshold)
- {
- /* Write threshold value to register */
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PIHT, threshold) ;
- }
- /**
- * @brief Gets if ambient light interrupts are enabled or not
- *
- * @return 1 if interrupts are enabled, 0 if not. 0xFF on error.
- */
- uint8_t getAmbientLightIntEnable(void)
- {
- uint8_t val;
- /* Read value from ENABLE register */
- val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE) ;
- /* Shift and mask out AIEN bit */
- val = (val >> 4) & 0b00000001;
- return val;
- }
- /**
- * @brief Turns ambient light interrupts on or off
- *
- * @param[in] enable 1 to enable interrupts, 0 to turn them off
- */
- void setAmbientLightIntEnable(uint8_t enable)
- {
- uint8_t val;
- /* Read value from ENABLE register */
- val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE);
- /* Set bits in register to given value */
- enable &= 0b00000001;
- enable = enable << 4;
- val &= 0b11101111;
- val |= enable;
- /* Write register value back into ENABLE register */
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ENABLE, val) ;
- }
- /**
- * @brief Gets if proximity interrupts are enabled or not
- *
- * @return 1 if interrupts are enabled, 0 if not. 0xFF on error.
- */
- uint8_t getProximityIntEnable(void)
- {
- uint8_t val;
- /* Read value from ENABLE register */
- val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE) ;
- /* Shift and mask out PIEN bit */
- val = (val >> 5) & 0b00000001;
- return val;
- }
- /**
- * @brief Turns proximity interrupts on or off
- *
- * @param[in] enable 1 to enable interrupts, 0 to turn them off
- */
- void setProximityIntEnable(uint8_t enable)
- {
- uint8_t val;
- /* Read value from ENABLE register */
- val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE) ;
- /* Set bits in register to given value */
- enable &= 0b00000001;
- enable = enable << 5;
- val &= 0b11011111;
- val |= enable;
- /* Write register value back into ENABLE register */
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ENABLE, val) ;
- }
- /**
- * @brief Gets if gesture interrupts are enabled or not
- *
- * @return 1 if interrupts are enabled, 0 if not. 0xFF on error.
- */
- uint8_t getGestureIntEnable(void)
- {
- uint8_t val;
- /* Read value from GCONF4 register */
- val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF4) ;
- /* Shift and mask out GIEN bit */
- val = (val >> 1) & 0b00000001;
- return val;
- }
- /**
- * @brief Turns gesture-related interrupts on or off
- *
- * @param[in] enable 1 to enable interrupts, 0 to turn them off
- */
- void setGestureIntEnable(uint8_t enable)
- {
- uint8_t val;
- /* Read value from GCONF4 register */
- val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF4) ;
- /* Set bits in register to given value */
- enable &= 0b00000001;
- enable = enable << 1;
- val &= 0b11111101;
- val |= enable;
- /* Write register value back into GCONF4 register */
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF4, val) ;
- }
- /**
- * @brief Clears the ambient light interrupt
- *
- */
- void clearAmbientLightInt(void)
- {
- uint8_t throwaway;
- throwaway = I2cRead8(APDS9960_I2C_ADDR, APDS9960_AICLEAR);
- }
- /**
- * @brief Clears the proximity interrupt
- *
- */
- void clearProximityInt(void)
- {
- uint8_t throwaway;
- throwaway = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PICLEAR) ;
- }
- /**
- * @brief Tells if the gesture state machine is currently running
- *
- * @return 1 if gesture state machine is running, 0 if not. 0xFF on error.
- */
- uint8_t getGestureMode(void)
- {
- uint8_t val;
- /* Read value from GCONF4 register */
- val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF4) ;
- /* Mask out GMODE bit */
- val &= 0b00000001;
- return val;
- }
- /**
- * @brief Tells the state machine to either enter or exit gesture state machine
- *
- * @param[in] mode 1 to enter gesture state machine, 0 to exit.
- */
- void setGestureMode(uint8_t mode)
- {
- uint8_t val;
- /* Read value from GCONF4 register */
- val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF4) ;
- /* Set bits in register to given value */
- mode &= 0b00000001;
- val &= 0b11111110;
- val |= mode;
- /* Write register value back into GCONF4 register */
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF4, val) ;
- }
- bool APDS9960_init(void)
- {
- /* Set default values for ambient light and proximity registers */
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ATIME, DEFAULT_ATIME) ;
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_WTIME, DEFAULT_WTIME) ;
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PPULSE, DEFAULT_PROX_PPULSE) ;
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_POFFSET_UR, DEFAULT_POFFSET_UR) ;
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_POFFSET_DL, DEFAULT_POFFSET_DL) ;
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG1, DEFAULT_CONFIG1) ;
- setLEDDrive(DEFAULT_LDRIVE);
- setProximityGain(DEFAULT_PGAIN);
- setAmbientLightGain(DEFAULT_AGAIN);
- setProxIntLowThresh(DEFAULT_PILT) ;
- setProxIntHighThresh(DEFAULT_PIHT);
- setLightIntLowThreshold(DEFAULT_AILT) ;
- setLightIntHighThreshold(DEFAULT_AIHT) ;
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PERS, DEFAULT_PERS) ;
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG2, DEFAULT_CONFIG2) ;
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG3, DEFAULT_CONFIG3) ;
- /* Set default values for gesture sense registers */
- setGestureEnterThresh(DEFAULT_GPENTH);
- setGestureExitThresh(DEFAULT_GEXTH) ;
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF1, DEFAULT_GCONF1) ;
- setGestureGain(DEFAULT_GGAIN) ;
- setGestureLEDDrive(DEFAULT_GLDRIVE) ;
- setGestureWaitTime(DEFAULT_GWTIME) ;
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GOFFSET_U, DEFAULT_GOFFSET) ;
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GOFFSET_D, DEFAULT_GOFFSET) ;
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GOFFSET_L, DEFAULT_GOFFSET) ;
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GOFFSET_R, DEFAULT_GOFFSET) ;
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GPULSE, DEFAULT_GPULSE) ;
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF3, DEFAULT_GCONF3) ;
- setGestureIntEnable(DEFAULT_GIEN);
- disablePower(); // go to sleep
- return true;
- }
- /*******************************************************************************
- * Public methods for controlling the APDS-9960
- ******************************************************************************/
- /**
- * @brief Reads and returns the contents of the ENABLE register
- *
- * @return Contents of the ENABLE register. 0xFF if error.
- */
- uint8_t getMode(void)
- {
- uint8_t enable_value;
- /* Read current ENABLE register */
- enable_value = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE) ;
- return enable_value;
- }
- /**
- * @brief Enables or disables a feature in the APDS-9960
- *
- * @param[in] mode which feature to enable
- * @param[in] enable ON (1) or OFF (0)
- */
- void setMode(uint8_t mode, uint8_t enable)
- {
- uint8_t reg_val;
- /* Read current ENABLE register */
- reg_val = getMode();
- /* Change bit(s) in ENABLE register */
- enable = enable & 0x01;
- if( mode >= 0 && mode <= 6 ) {
- if (enable) {
- reg_val |= (1 << mode);
- } else {
- reg_val &= ~(1 << mode);
- }
- } else if( mode == ALL ) {
- if (enable) {
- reg_val = 0x7F;
- } else {
- reg_val = 0x00;
- }
- }
- /* Write value back to ENABLE register */
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ENABLE, reg_val) ;
- }
- /**
- * @brief Starts the light (R/G/B/Ambient) sensor on the APDS-9960
- *
- * no interrupts
- */
- void enableLightSensor(void)
- {
- /* Set default gain, interrupts, enable power, and enable sensor */
- setAmbientLightGain(DEFAULT_AGAIN);
- setAmbientLightIntEnable(0);
- enablePower() ;
- setMode(AMBIENT_LIGHT, 1) ;
- }
- /**
- * @brief Ends the light sensor on the APDS-9960
- *
- */
- void disableLightSensor(void)
- {
- setAmbientLightIntEnable(0) ;
- setMode(AMBIENT_LIGHT, 0) ;
- }
- /**
- * @brief Starts the proximity sensor on the APDS-9960
- *
- * no interrupts
- */
- void enableProximitySensor(void)
- {
- /* Set default gain, LED, interrupts, enable power, and enable sensor */
- setProximityGain(DEFAULT_PGAIN);
- setLEDDrive(DEFAULT_LDRIVE) ;
- setProximityIntEnable(0) ;
- enablePower();
- setMode(PROXIMITY, 1) ;
- }
- /**
- * @brief Ends the proximity sensor on the APDS-9960
- *
- */
- void disableProximitySensor(void)
- {
- setProximityIntEnable(0) ;
- setMode(PROXIMITY, 0) ;
- }
- /**
- * @brief Starts the gesture recognition engine on the APDS-9960
- *
- * no interrupts
- */
- void enableGestureSensor(void)
- {
- /* Enable gesture mode
- Set ENABLE to 0 (power off)
- Set WTIME to 0xFF
- Set AUX to LED_BOOST_300
- Enable PON, WEN, PEN, GEN in ENABLE
- */
- resetGestureParameters();
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_WTIME, 0xFF) ;
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PPULSE, DEFAULT_GESTURE_PPULSE) ;
- setLEDBoost(LED_BOOST_100); // tip from jonn26 - 100 for 300 ---- 200 from Adafruit
- setGestureIntEnable(0) ;
- setGestureMode(1);
- enablePower() ;
- setMode(WAIT, 1) ;
- setMode(PROXIMITY, 1) ;
- setMode(GESTURE, 1);
- }
- /**
- * @brief Ends the gesture recognition engine on the APDS-9960
- *
- */
- void disableGestureSensor(void)
- {
- resetGestureParameters();
- setGestureIntEnable(0) ;
- setGestureMode(0) ;
- setMode(GESTURE, 0) ;
- }
- /**
- * @brief Determines if there is a gesture available for reading
- *
- * @return True if gesture available. False otherwise.
- */
- bool isGestureAvailable(void)
- {
- uint8_t val;
- /* Read value from GSTATUS register */
- val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GSTATUS) ;
- /* Shift and mask out GVALID bit */
- val &= APDS9960_GVALID;
- /* Return true/false based on GVALID bit */
- if( val == 1) {
- return true;
- } else {
- return false;
- }
- }
- /**
- * @brief Processes a gesture event and returns best guessed gesture
- *
- * @return Number corresponding to gesture. -1 on error.
- */
- int16_t readGesture(void)
- {
- uint8_t fifo_level = 0;
- uint8_t bytes_read = 0;
- uint8_t fifo_data[128];
- uint8_t gstatus;
- uint16_t motion;
- uint16_t i;
- uint8_t gesture_loop_counter = 0; // don't loop forever later
- /* Make sure that power and gesture is on and data is valid */
- if( !isGestureAvailable() || !(getMode() & 0b01000001) ) {
- return DIR_NONE;
- }
- /* Keep looping as long as gesture data is valid */
- while(1) {
- if (gesture_loop_counter == APDS9960_MAX_GESTURE_CYCLES){ // We will escape after a few loops
- disableGestureSensor(); // stop the sensor to prevent problems with power consumption/blocking and return to the main loop
- APDS9960_overload = true; // we report this as "long"-gesture
- snprintf_P(log_data, sizeof(log_data), PSTR("Sensor overload"));
- AddLog(LOG_LEVEL_DEBUG);
- }
- gesture_loop_counter += 1;
- /* Wait some time to collect next batch of FIFO data */
- delay(FIFO_PAUSE_TIME);
- /* Get the contents of the STATUS register. Is data still valid? */
- gstatus = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GSTATUS);
- /* If we have valid data, read in FIFO */
- if( (gstatus & APDS9960_GVALID) == APDS9960_GVALID ) {
- /* Read the current FIFO level */
- fifo_level = I2cRead8(APDS9960_I2C_ADDR,APDS9960_GFLVL) ;
- /* If there's stuff in the FIFO, read it into our data block */
- if( fifo_level > 0) {
- bytes_read = wireReadDataBlock( APDS9960_GFIFO_U,
- (uint8_t*)fifo_data,
- (fifo_level * 4) );
- if( bytes_read == -1 ) {
- return ERROR;
- }
- /* If at least 1 set of data, sort the data into U/D/L/R */
- if( bytes_read >= 4 ) {
- for( i = 0; i < bytes_read; i += 4 ) {
- gesture_data_.u_data[gesture_data_.index] = \
- fifo_data[i + 0];
- gesture_data_.d_data[gesture_data_.index] = \
- fifo_data[i + 1];
- gesture_data_.l_data[gesture_data_.index] = \
- fifo_data[i + 2];
- gesture_data_.r_data[gesture_data_.index] = \
- fifo_data[i + 3];
- gesture_data_.index++;
- gesture_data_.total_gestures++;
- }
- /* Filter and process gesture data. Decode near/far state */
- if( processGestureData() ) {
- if( decodeGesture() ) {
- //***TODO: U-Turn Gestures
- }
- }
- /* Reset data */
- gesture_data_.index = 0;
- gesture_data_.total_gestures = 0;
- }
- }
- } else {
- /* Determine best guessed gesture and clean up */
- delay(FIFO_PAUSE_TIME);
- decodeGesture();
- motion = gesture_motion_;
- resetGestureParameters();
- return motion;
- }
- }
- }
- /**
- * Turn the APDS-9960 on
- *
- */
- void enablePower(void)
- {
- setMode(POWER, 1) ;
- }
- /**
- * Turn the APDS-9960 off
- *
- */
- void disablePower(void)
- {
- setMode(POWER, 0) ;
- }
- /*******************************************************************************
- * Ambient light and color sensor controls
- ******************************************************************************/
- /**
- * @brief Reads the ARGB-Data and fills color_data
- *
- */
- void readAllColorAndProximityData(void)
- {
- if (I2cReadBuffer(APDS9960_I2C_ADDR, APDS9960_CDATAL, (uint8_t *) &color_data, (uint16_t)9))
- {
- // not absolutely shure, if this is a correct way to do this, but it is very short
- // we fill the struct byte by byte
- }
- }
- /*******************************************************************************
- * High-level gesture controls
- ******************************************************************************/
- /**
- * @brief Resets all the parameters in the gesture data member
- */
- void resetGestureParameters(void)
- {
- gesture_data_.index = 0;
- gesture_data_.total_gestures = 0;
- gesture_ud_delta_ = 0;
- gesture_lr_delta_ = 0;
- gesture_ud_count_ = 0;
- gesture_lr_count_ = 0;
- gesture_state_ = 0;
- gesture_motion_ = DIR_NONE;
- }
- /**
- * @brief Processes the raw gesture data to determine swipe direction
- *
- * @return True if near or far state seen. False otherwise.
- */
- bool processGestureData(void)
- {
- uint8_t u_first = 0;
- uint8_t d_first = 0;
- uint8_t l_first = 0;
- uint8_t r_first = 0;
- uint8_t u_last = 0;
- uint8_t d_last = 0;
- uint8_t l_last = 0;
- uint8_t r_last = 0;
- uint16_t ud_ratio_first;
- uint16_t lr_ratio_first;
- uint16_t ud_ratio_last;
- uint16_t lr_ratio_last;
- uint16_t ud_delta;
- uint16_t lr_delta;
- uint16_t i;
- /* If we have less than 4 total gestures, that's not enough */
- if( gesture_data_.total_gestures <= 4 ) {
- return false;
- }
- /* Check to make sure our data isn't out of bounds */
- if( (gesture_data_.total_gestures <= 32) && \
- (gesture_data_.total_gestures > 0) ) {
- /* Find the first value in U/D/L/R above the threshold */
- for( i = 0; i < gesture_data_.total_gestures; i++ ) {
- if( (gesture_data_.u_data[i] > GESTURE_THRESHOLD_OUT) &&
- (gesture_data_.d_data[i] > GESTURE_THRESHOLD_OUT) &&
- (gesture_data_.l_data[i] > GESTURE_THRESHOLD_OUT) &&
- (gesture_data_.r_data[i] > GESTURE_THRESHOLD_OUT) ) {
- u_first = gesture_data_.u_data[i];
- d_first = gesture_data_.d_data[i];
- l_first = gesture_data_.l_data[i];
- r_first = gesture_data_.r_data[i];
- break;
- }
- }
- /* If one of the _first values is 0, then there is no good data */
- if( (u_first == 0) || (d_first == 0) || \
- (l_first == 0) || (r_first == 0) ) {
- return false;
- }
- /* Find the last value in U/D/L/R above the threshold */
- for( i = gesture_data_.total_gestures - 1; i >= 0; i-- ) {
- if( (gesture_data_.u_data[i] > GESTURE_THRESHOLD_OUT) &&
- (gesture_data_.d_data[i] > GESTURE_THRESHOLD_OUT) &&
- (gesture_data_.l_data[i] > GESTURE_THRESHOLD_OUT) &&
- (gesture_data_.r_data[i] > GESTURE_THRESHOLD_OUT) ) {
- u_last = gesture_data_.u_data[i];
- d_last = gesture_data_.d_data[i];
- l_last = gesture_data_.l_data[i];
- r_last = gesture_data_.r_data[i];
- break;
- }
- }
- }
- /* Calculate the first vs. last ratio of up/down and left/right */
- ud_ratio_first = ((u_first - d_first) * 100) / (u_first + d_first);
- lr_ratio_first = ((l_first - r_first) * 100) / (l_first + r_first);
- ud_ratio_last = ((u_last - d_last) * 100) / (u_last + d_last);
- lr_ratio_last = ((l_last - r_last) * 100) / (l_last + r_last);
- /* Determine the difference between the first and last ratios */
- ud_delta = ud_ratio_last - ud_ratio_first;
- lr_delta = lr_ratio_last - lr_ratio_first;
- /* Accumulate the UD and LR delta values */
- gesture_ud_delta_ += ud_delta;
- gesture_lr_delta_ += lr_delta;
- /* Determine U/D gesture */
- if( gesture_ud_delta_ >= GESTURE_SENSITIVITY_1 ) {
- gesture_ud_count_ = 1;
- } else if( gesture_ud_delta_ <= -GESTURE_SENSITIVITY_1 ) {
- gesture_ud_count_ = -1;
- } else {
- gesture_ud_count_ = 0;
- }
- /* Determine L/R gesture */
- if( gesture_lr_delta_ >= GESTURE_SENSITIVITY_1 ) {
- gesture_lr_count_ = 1;
- } else if( gesture_lr_delta_ <= -GESTURE_SENSITIVITY_1 ) {
- gesture_lr_count_ = -1;
- } else {
- gesture_lr_count_ = 0;
- }
- return false;
- }
- /**
- * @brief Determines swipe direction or near/far state
- *
- * @return True if near/far event. False otherwise.
- */
- bool decodeGesture(void)
- {
- /* Determine swipe direction */
- if( (gesture_ud_count_ == -1) && (gesture_lr_count_ == 0) ) {
- gesture_motion_ = DIR_UP;
- } else if( (gesture_ud_count_ == 1) && (gesture_lr_count_ == 0) ) {
- gesture_motion_ = DIR_DOWN;
- } else if( (gesture_ud_count_ == 0) && (gesture_lr_count_ == 1) ) {
- gesture_motion_ = DIR_RIGHT;
- } else if( (gesture_ud_count_ == 0) && (gesture_lr_count_ == -1) ) {
- gesture_motion_ = DIR_LEFT;
- } else if( (gesture_ud_count_ == -1) && (gesture_lr_count_ == 1) ) {
- if( abs(gesture_ud_delta_) > abs(gesture_lr_delta_) ) {
- gesture_motion_ = DIR_UP;
- } else {
- gesture_motion_ = DIR_RIGHT;
- }
- } else if( (gesture_ud_count_ == 1) && (gesture_lr_count_ == -1) ) {
- if( abs(gesture_ud_delta_) > abs(gesture_lr_delta_) ) {
- gesture_motion_ = DIR_DOWN;
- } else {
- gesture_motion_ = DIR_LEFT;
- }
- } else if( (gesture_ud_count_ == -1) && (gesture_lr_count_ == -1) ) {
- if( abs(gesture_ud_delta_) > abs(gesture_lr_delta_) ) {
- gesture_motion_ = DIR_UP;
- } else {
- gesture_motion_ = DIR_LEFT;
- }
- } else if( (gesture_ud_count_ == 1) && (gesture_lr_count_ == 1) ) {
- if( abs(gesture_ud_delta_) > abs(gesture_lr_delta_) ) {
- gesture_motion_ = DIR_DOWN;
- } else {
- gesture_motion_ = DIR_RIGHT;
- }
- } else {
- return false;
- }
- return true;
- }
- void handleGesture(void) {
- if (isGestureAvailable() ) {
- switch (readGesture()) {
- case DIR_UP:
- snprintf_P(log_data, sizeof(log_data), PSTR("UP"));
- snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Up"));
- break;
- case DIR_DOWN:
- snprintf_P(log_data, sizeof(log_data), PSTR("DOWN"));
- snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Down"));
- break;
- case DIR_LEFT:
- snprintf_P(log_data, sizeof(log_data), PSTR("LEFT"));
- snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Left"));
- break;
- case DIR_RIGHT:
- snprintf_P(log_data, sizeof(log_data), PSTR("RIGHT"));
- snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Right"));
- break;
- default:
- if(APDS9960_overload)
- {
- snprintf_P(log_data, sizeof(log_data), PSTR("LONG"));
- snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Long"));
- }
- else{
- snprintf_P(log_data, sizeof(log_data), PSTR("NONE"));
- snprintf_P(currentGesture, sizeof(currentGesture), PSTR("None"));
- }
- }
- AddLog(LOG_LEVEL_DEBUG);
- mqtt_data[0] = '\0';
- if (MqttShowSensor()) {
- MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain);
- #ifdef USE_RULES
- RulesTeleperiod(); // Allow rule based HA messages
- #endif // USE_RULES
- }
- }
- }
- void APDS9960_adjustATime(void) // not really used atm
- {
- //readAllColorAndProximityData();
- I2cValidRead16LE(&color_data.a, APDS9960_I2C_ADDR, APDS9960_CDATAL);
- //disablePower();
- if (color_data.a < (uint16_t)20){
- APDS9960_aTime = 0x40;
- }
- else if (color_data.a < (uint16_t)40){
- APDS9960_aTime = 0x80;
- }
- else if (color_data.a < (uint16_t)50){
- APDS9960_aTime = DEFAULT_ATIME;
- }
- else if (color_data.a < (uint16_t)70){
- APDS9960_aTime = 0xc0;
- }
- if (color_data.a < 200){
- APDS9960_aTime = 0xe9;
- }
- /* if (color_data.a < 10000){
- APDS9960_aTime = 0xF0;
- }*/
- else{
- APDS9960_aTime = 0xff;
- }
- //disableLightSensor();
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ATIME, APDS9960_aTime);
- enablePower();
- enableLightSensor();
- delay(20);
- }
- void APDS9960_loop(void)
- {
- if (recovery_loop_counter > 0){
- recovery_loop_counter -= 1;
- }
- if (recovery_loop_counter == 1 && APDS9960_overload){ //restart sensor just before the end of recovery from long press
- enableGestureSensor();
- APDS9960_overload = false;
- snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"Gesture\":\"On\"}"));
- MqttPublishPrefixTopic_P(RESULT_OR_TELE, mqtt_data); // only after the long break we report, that we are online again
- gesture_mode = 1;
- }
- if (gesture_mode) {
- if (recovery_loop_counter == 0){
- handleGesture();
- if (APDS9960_overload)
- {
- disableGestureSensor();
- recovery_loop_counter = APDS9960_LONG_RECOVERY; // long pause after overload/long press - number of stateloops
- snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"Gesture\":\"Off\"}"));
- MqttPublishPrefixTopic_P(RESULT_OR_TELE, mqtt_data);
- gesture_mode = 0;
- }
- }
- }
- }
- bool APDS9960_detect(void)
- {
- if (APDS9960type) {
- return true;
- }
- boolean success = false;
- APDS9960type = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ID);
- if (APDS9960type == APDS9960_CHIPID_1 || APDS9960type == APDS9960_CHIPID_2) {
- strcpy_P(APDS9960stype, PSTR("APDS9960"));
- snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, APDS9960stype, APDS9960_I2C_ADDR);
- AddLog(LOG_LEVEL_DEBUG);
- if (APDS9960_init()) {
- success = true;
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "APDS9960 initialized"));
- enableProximitySensor();
- enableGestureSensor();
- }
- }
- else {
- if (APDS9960type == APDS9930_CHIPID_1 || APDS9960type == APDS9930_CHIPID_2) {
- snprintf_P(log_data, sizeof(log_data), PSTR("APDS9930 found at address 0x%x, unsupported chip"), APDS9960_I2C_ADDR);
- AddLog(LOG_LEVEL_DEBUG);
- }
- else{
- snprintf_P(log_data, sizeof(log_data), PSTR("APDS9960 not found at address 0x%x"), APDS9960_I2C_ADDR);
- AddLog(LOG_LEVEL_DEBUG);
- }
- }
- currentGesture[0] = '\0';
- return success;
- }
- /*********************************************************************************************\
- * Presentation
- \*********************************************************************************************/
- void APDS9960_show(boolean json)
- {
- if (!APDS9960type) {
- return;
- }
- if (!gesture_mode && !APDS9960_overload) {
- char red_chr[10];
- char green_chr[10];
- char blue_chr[10];
- char ambient_chr[10];
- char cct_chr[10];
- char prox_chr[10];
- readAllColorAndProximityData();
- sprintf (ambient_chr, "%u", color_data.a/4);
- sprintf (red_chr, "%u", color_data.r);
- sprintf (green_chr, "%u", color_data.g);
- sprintf (blue_chr, "%u", color_data.b );
- sprintf (prox_chr, "%u", color_data.p );
- /* disableLightSensor();
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ATIME, DEFAULT_ATIME); // reset to default
- enableLightSensor();*/
- calculateColorTemperature(); // and calculate Lux
- sprintf (cct_chr, "%u", color_data.cct);
- if (json) {
- snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"%s\":{\"Red\":%s,\"Green\":%s,\"Blue\":%s,\"Ambient\":%s,\"CCT\":%s,\"Proximity\":%s}"),
- mqtt_data, APDS9960stype, red_chr, green_chr, blue_chr, ambient_chr, cct_chr, prox_chr);
- #ifdef USE_WEBSERVER
- } else {
- snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_APDS_9960_SNS, mqtt_data, red_chr, green_chr, blue_chr, ambient_chr, cct_chr, prox_chr );
- #endif // USE_WEBSERVER
- }
- }
- else {
- if (json && (currentGesture[0] != '\0' )) {
- snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"%s\":{\"%s\":1}"), mqtt_data, APDS9960stype, currentGesture);
- currentGesture[0] = '\0';
- }
- }
- }
- /*********************************************************************************************\
- * Command Sensor27
- *
- * Command | Payload | Description
- * ---------|---------|--------------------------
- * Sensor27 | | Show current gesture mode
- * Sensor27 | 0 / Off | Disable gesture mode
- * Sensor27 | 1 / On | Enable gesture mode
- * Sensor27 | 2 / On | Enable gesture mode with half gain
- \*********************************************************************************************/
- bool APDS9960CommandSensor(void)
- {
- boolean serviced = true;
- switch (XdrvMailbox.payload) {
- case 0: // Off
- disableGestureSensor();
- gesture_mode = 0;
- enableLightSensor();
- APDS9960_overload = false; // prevent unwanted re-enabling
- break;
- case 1: // On with default gain of 4x
- if (APDS9960type) {
- setGestureGain(DEFAULT_GGAIN);
- setProximityGain(DEFAULT_PGAIN);
- disableLightSensor();
- enableGestureSensor();
- gesture_mode = 1;
- }
- break;
- case 2: // gain of 2x , needed for some models
- if (APDS9960type) {
- setGestureGain(GGAIN_2X);
- setProximityGain(PGAIN_2X);
- disableLightSensor();
- enableGestureSensor();
- gesture_mode = 1;
- }
- break;
- default:
- int temp_aTime = (uint8_t)XdrvMailbox.payload;
- if (temp_aTime > 2 && temp_aTime < 256){
- disablePower();
- I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ATIME, temp_aTime);
- enablePower();
- enableLightSensor();
- }
- break;
- }
- snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_SENSOR_INDEX_SVALUE, XSNS_27, GetStateText(gesture_mode));
- return serviced;
- }
- /*********************************************************************************************\
- * Interface
- \*********************************************************************************************/
- boolean Xsns27(byte function)
- {
- boolean result = false;
- if (i2c_flg) {
- if (FUNC_INIT == function) {
- APDS9960_detect();
- } else if (APDS9960type) {
- switch (function) {
- case FUNC_EVERY_50_MSECOND:
- APDS9960_loop();
- break;
- case FUNC_COMMAND:
- if (XSNS_27 == XdrvMailbox.index) {
- result = APDS9960CommandSensor();
- }
- break;
- case FUNC_JSON_APPEND:
- APDS9960_show(1);
- break;
- #ifdef USE_WEBSERVER
- case FUNC_WEB_APPEND:
- APDS9960_show(0);
- break;
- #endif // USE_WEBSERVER
- }
- }
- }
- return result;
- }
- #endif // USE_APDS9960
- #endif // USE_I2C
|