From f4139b27c8866a29353b4be6ccdbdc6b86e34794 Mon Sep 17 00:00:00 2001 From: Florian Eitel Date: Sat, 3 Aug 2019 20:34:17 +0200 Subject: [PATCH] Update firmware libraries --- .../Adafruit_APDS9960.cpp | 869 ++++++++------- .../Adafruit_APDS9960.h | 993 +++++++++--------- .../Adafruit_APDS9960_Library/README.md | 18 + .../assets/board.jpg | Bin 0 -> 291777 bytes .../code-of-conduct.md | 127 +++ .../library.properties | 2 +- .../Adafruit_APDS9960_Library/license.txt | 26 + .../Adafruit_BME280.cpp | 892 ++++++++-------- .../Adafruit_BME280_Library/Adafruit_BME280.h | 511 ++++----- .../libraries/Adafruit_BME280_Library/LICENSE | 27 + .../Adafruit_BME280_Library/README.md | 36 +- .../Adafruit_BME280_Library/assets/board.jpg | Bin 0 -> 441377 bytes .../advancedsettings/advancedsettings.ino | 6 +- .../examples/bme280test/bme280test.ino | 13 +- .../library.properties | 2 +- .../Adafruit_Unified_Sensor/Adafruit_Sensor.h | 12 +- .../Adafruit_Unified_Sensor/README.md | 13 +- .../library.properties | 3 +- firmware/sensors.ino | 3 +- 19 files changed, 1968 insertions(+), 1585 deletions(-) create mode 100644 firmware/libraries/Adafruit_APDS9960_Library/README.md create mode 100644 firmware/libraries/Adafruit_APDS9960_Library/assets/board.jpg create mode 100644 firmware/libraries/Adafruit_APDS9960_Library/code-of-conduct.md create mode 100644 firmware/libraries/Adafruit_APDS9960_Library/license.txt create mode 100644 firmware/libraries/Adafruit_BME280_Library/LICENSE create mode 100644 firmware/libraries/Adafruit_BME280_Library/assets/board.jpg mode change 100755 => 100644 firmware/sensors.ino diff --git a/firmware/libraries/Adafruit_APDS9960_Library/Adafruit_APDS9960.cpp b/firmware/libraries/Adafruit_APDS9960_Library/Adafruit_APDS9960.cpp index 61eadff..8650b33 100644 --- a/firmware/libraries/Adafruit_APDS9960_Library/Adafruit_APDS9960.cpp +++ b/firmware/libraries/Adafruit_APDS9960_Library/Adafruit_APDS9960.cpp @@ -1,94 +1,96 @@ -/**************************************************************************/ -/*! - @file Adafruit_APDS9960.cpp - @author Ladyada, Dean Miller (Adafruit Industries) - - @section LICENSE - - Software License Agreement (BSD License) - - Copyright (c) 2017, Adafruit Industries - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. 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. - 3. Neither the name of the copyright holders nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 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. -*/ -/**************************************************************************/ +/*! + * @file Adafruit_APDS9960.cpp + * + * @mainpage Adafruit APDS9960 Proximity, Light, RGB, and Gesture Sensor + * + * @section author Author + * + * Ladyada, Dean Miller (Adafruit Industries) + * + * @section license License + * + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Adafruit Industries + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holders nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 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 __AVR - #include +#include #elif defined(ESP8266) - #include +#include #endif -#include #include +#include #include "Adafruit_APDS9960.h" -/*========================================================================*/ -/* PRIVATE FUNCTIONS */ -/*========================================================================*/ - -/**************************************************************************/ /*! - @brief Implements missing powf function -*/ -/**************************************************************************/ -float powf(const float x, const float y) -{ + * @brief Implements missing powf function + * @param x + * Base number + * @param y + * Exponent + * @return x raised to the power of y + */ +float powf(const float x, const float y) { return (float)(pow((double)x, (double)y)); } -/**************************************************************************/ /*! - Enables the device - Disables the device (putting it in lower power sleep mode) -*/ -/**************************************************************************/ -void Adafruit_APDS9960::enable(boolean en) -{ + * @brief Enables the device + * Disables the device (putting it in lower power sleep mode) + * @param en + * Enable (True/False) + */ +void Adafruit_APDS9960::enable(boolean en) { _enable.PON = en; this->write8(APDS9960_ENABLE, _enable.get()); } -/*========================================================================*/ -/* PUBLIC FUNCTIONS */ -/*========================================================================*/ - -/**************************************************************************/ /*! - Initializes I2C and configures the sensor (call this function before - doing anything else) -*/ -/**************************************************************************/ -boolean Adafruit_APDS9960::begin(uint16_t iTimeMS, apds9960AGain_t aGain, uint8_t addr) -{ + * @brief Initializes I2C and configures the sensor + * @param iTimeMS + * Integration time + * @param aGain + * Gain + * @param addr + * I2C address + * @param *theWire + * Wire object + * @return True if initialization was successful, otherwise false. + */ +boolean Adafruit_APDS9960::begin(uint16_t iTimeMS, apds9960AGain_t aGain, + uint8_t addr, TwoWire *theWire) { + _wire = theWire; _i2c_init(); _i2caddr = addr; - + /* Make sure we're actually connected */ uint8_t x = read8(APDS9960_ID); - if (x != 0xAB) - { + if (x != 0xAB) { return false; } @@ -100,7 +102,7 @@ boolean Adafruit_APDS9960::begin(uint16_t iTimeMS, apds9960AGain_t aGain, uint8_ enableGesture(false); enableProximity(false); enableColor(false); - + disableColorInterrupt(); disableProximityInterrupt(); clearInterrupt(); @@ -110,43 +112,47 @@ boolean Adafruit_APDS9960::begin(uint16_t iTimeMS, apds9960AGain_t aGain, uint8_ delay(10); enable(true); delay(10); - - //default to all gesture dimensions + + // default to all gesture dimensions setGestureDimensions(APDS9960_DIMENSIONS_ALL); setGestureFIFOThreshold(APDS9960_GFIFO_4); setGestureGain(APDS9960_GGAIN_4); setGestureProximityThreshold(50); resetCounts(); - + _gpulse.GPLEN = APDS9960_GPULSE_32US; - _gpulse.GPULSE = 9; //10 pulses + _gpulse.GPULSE = 9; // 10 pulses this->write8(APDS9960_GPULSE, _gpulse.get()); - + return true; } - -/**************************************************************************/ + /*! - Sets the integration time for the ADC of the APDS9960, in millis -*/ -/**************************************************************************/ -void Adafruit_APDS9960::setADCIntegrationTime(uint16_t iTimeMS) -{ + * @brief Sets the integration time for the ADC of the APDS9960, in millis + * @param iTimeMS + * Integration time + */ +void Adafruit_APDS9960::setADCIntegrationTime(uint16_t iTimeMS) { float temp; // convert ms into 2.78ms increments temp = iTimeMS; temp /= 2.78; temp = 256 - temp; - if (temp > 255) temp = 255; - if (temp < 0) temp = 0; - + if (temp > 255) + temp = 255; + if (temp < 0) + temp = 0; + /* Update the timing register */ write8(APDS9960_ATIME, (uint8_t)temp); } -float Adafruit_APDS9960::getADCIntegrationTime(void) -{ +/*! + * @brief Returns the integration time for the ADC of the APDS9960, in millis + * @return Integration time + */ +float Adafruit_APDS9960::getADCIntegrationTime() { float temp; temp = read8(APDS9960_ATIME); @@ -157,158 +163,221 @@ float Adafruit_APDS9960::getADCIntegrationTime(void) return temp; } -/**************************************************************************/ /*! - Adjusts the color/ALS gain on the APDS9960 (adjusts the sensitivity to light) -*/ -/**************************************************************************/ -void Adafruit_APDS9960::setADCGain(apds9960AGain_t aGain) -{ + * @brief Adjusts the color/ALS gain on the APDS9960 (adjusts the sensitivity + * to light) + * @param aGain + * Gain + */ +void Adafruit_APDS9960::setADCGain(apds9960AGain_t aGain) { _control.AGAIN = aGain; /* Update the timing register */ write8(APDS9960_CONTROL, _control.get()); } -apds9960AGain_t Adafruit_APDS9960::getADCGain(void) -{ - return (apds9960AGain_t) ( read8(APDS9960_CONTROL) & 0x03 ); +/*! + * @brief Returns the ADC gain + * @return ADC gain + */ +apds9960AGain_t Adafruit_APDS9960::getADCGain() { + return (apds9960AGain_t)(read8(APDS9960_CONTROL) & 0x03); } - -/**************************************************************************/ /*! - Adjusts the Proximity gain on the APDS9960 -*/ -/**************************************************************************/ -void Adafruit_APDS9960::setProxGain(apds9960PGain_t pGain) -{ + * @brief Adjusts the Proximity gain on the APDS9960 + * @param pGain + * Gain + */ +void Adafruit_APDS9960::setProxGain(apds9960PGain_t pGain) { _control.PGAIN = pGain; /* Update the timing register */ write8(APDS9960_CONTROL, _control.get()); } -apds9960PGain_t Adafruit_APDS9960::getProxGain(void) -{ - return (apds9960PGain_t) ( read8(APDS9960_CONTROL) & 0x0C ); +/*! + * @brief Returns the Proximity gain on the APDS9960 + * @return Proxmity gain + */ +apds9960PGain_t Adafruit_APDS9960::getProxGain() { + return (apds9960PGain_t)(read8(APDS9960_CONTROL) & 0x0C); } +/*! + * @brief Sets number of proxmity pulses + * @param pLen + * Pulse Length + * @param pulses + * Number of pulses + */ void Adafruit_APDS9960::setProxPulse(apds9960PPulseLen_t pLen, uint8_t pulses) { - if (pulses < 1) pulses = 1; - if (pulses > 64) pulses = 64; + if (pulses < 1) + pulses = 1; + if (pulses > 64) + pulses = 64; pulses--; - + _ppulse.PPLEN = pLen; _ppulse.PPULSE = pulses; - - write8(APDS9960_PPULSE, _ppulse.get()); -} -/**************************************************************************/ + write8(APDS9960_PPULSE, _ppulse.get()); +} + /*! - Enable proximity readings on APDS9960 -*/ -/**************************************************************************/ -void Adafruit_APDS9960::enableProximity(boolean en) -{ + * @brief Enable proximity readings on APDS9960 + * @param en + * Enable (True/False) + */ +void Adafruit_APDS9960::enableProximity(boolean en) { _enable.PEN = en; write8(APDS9960_ENABLE, _enable.get()); } +/*! + * @brief Enable proximity interrupts + */ void Adafruit_APDS9960::enableProximityInterrupt() { - _enable.PIEN = 1; - write8(APDS9960_ENABLE, _enable.get()); - clearInterrupt(); + _enable.PIEN = 1; + write8(APDS9960_ENABLE, _enable.get()); + clearInterrupt(); } +/*! + * @brief Disable proximity interrupts + */ void Adafruit_APDS9960::disableProximityInterrupt() { - _enable.PIEN = 0; - write8(APDS9960_ENABLE, _enable.get()); + _enable.PIEN = 0; + write8(APDS9960_ENABLE, _enable.get()); } -void Adafruit_APDS9960::setProximityInterruptThreshold(uint8_t low, uint8_t high, uint8_t persistance){ - write8(APDS9960_PILT, low); - write8(APDS9960_PIHT, high); - - if (persistance > 7) persistance = 7; - _pers.PPERS = persistance; - write8(APDS9960_PERS,_pers.get()); +/*! + * @brief Set proxmity interrupt thresholds + * @param low + * Low threshold + * @param high + * High threshold + * @param persistance + * Persistance + */ +void Adafruit_APDS9960::setProximityInterruptThreshold(uint8_t low, + uint8_t high, + uint8_t persistance) { + write8(APDS9960_PILT, low); + write8(APDS9960_PIHT, high); + + if (persistance > 7) + persistance = 7; + _pers.PPERS = persistance; + write8(APDS9960_PERS, _pers.get()); } -bool Adafruit_APDS9960::getProximityInterrupt() -{ - _status.set(this->read8(APDS9960_STATUS)); - return _status.PINT; +/*! + * @brief Returns proxmity interrupt status + * @return True if enabled, false otherwise. + */ +bool Adafruit_APDS9960::getProximityInterrupt() { + _status.set(this->read8(APDS9960_STATUS)); + return _status.PINT; }; -/**************************************************************************/ /*! - Read proximity data -*/ -/**************************************************************************/ -uint8_t Adafruit_APDS9960::readProximity(void) -{ - return read8(APDS9960_PDATA); -} + * @brief Read proximity data + * @return Proximity + */ +uint8_t Adafruit_APDS9960::readProximity() { return read8(APDS9960_PDATA); } - -bool Adafruit_APDS9960::gestureValid() -{ - _gstatus.set(this->read8(APDS9960_GSTATUS)); - return _gstatus.GVALID; -} - -void Adafruit_APDS9960::setGestureDimensions(uint8_t dims) -{ - _gconf3.GDIMS = dims; - this->write8(APDS9960_GCONF3, _gconf3.get()); -} - -void Adafruit_APDS9960::setGestureFIFOThreshold(uint8_t thresh) -{ - _gconf1.GFIFOTH = thresh; - this->write8(APDS9960_GCONF1, _gconf1.get()); -} - -void Adafruit_APDS9960::setGestureGain(uint8_t gain) -{ - _gconf2.GGAIN = gain; - this->write8(APDS9960_GCONF2, _gconf2.get()); -} - -void Adafruit_APDS9960::setGestureProximityThreshold(uint8_t thresh) -{ - this->write8(APDS9960_GPENTH, thresh); -} - -void Adafruit_APDS9960::setGestureOffset(uint8_t offset_up, uint8_t offset_down, uint8_t offset_left, uint8_t offset_right) -{ - this->write8(APDS9960_GOFFSET_U, offset_up); - this->write8(APDS9960_GOFFSET_D, offset_down); - this->write8(APDS9960_GOFFSET_L, offset_left); - this->write8(APDS9960_GOFFSET_R, offset_right); -} - -/**************************************************************************/ /*! - Enable gesture readings on APDS9960 -*/ -/**************************************************************************/ -void Adafruit_APDS9960::enableGesture(boolean en) -{ - if(!en){ - _gconf4.GMODE = 0; - write8(APDS9960_GCONF4, _gconf4.get()); + * @brief Returns validity status of a gesture + * @return Status (True/False) + */ +bool Adafruit_APDS9960::gestureValid() { + _gstatus.set(this->read8(APDS9960_GSTATUS)); + return _gstatus.GVALID; +} + +/*! + * @brief Sets gesture dimensions + * @param dims + * Dimensions (APDS9960_DIMENSIONS_ALL, APDS9960_DIMENSIONS_UP_DOWM, + * APDS9960_DIMENSIONS_UP_DOWN, APGS9960_DIMENSIONS_LEFT_RIGHT) + */ +void Adafruit_APDS9960::setGestureDimensions(uint8_t dims) { + _gconf3.GDIMS = dims; + this->write8(APDS9960_GCONF3, _gconf3.get()); +} + +/*! + * @brief Sets gesture FIFO Threshold + * @param thresh + * Threshold (APDS9960_GFIFO_1, APDS9960_GFIFO_4, APDS9960_GFIFO_8, + * APDS9960_GFIFO_16) + */ +void Adafruit_APDS9960::setGestureFIFOThreshold(uint8_t thresh) { + _gconf1.GFIFOTH = thresh; + this->write8(APDS9960_GCONF1, _gconf1.get()); +} + +/*! + * @brief Sets gesture sensor gain + * @param gain + * Gain (APDS9960_GAIN_1, APDS9960_GAIN_2, APDS9960_GAIN_4, + * APDS9960_GAIN_8) + */ +void Adafruit_APDS9960::setGestureGain(uint8_t gain) { + _gconf2.GGAIN = gain; + this->write8(APDS9960_GCONF2, _gconf2.get()); +} + +/*! + * @brief Sets gesture sensor threshold + * @param thresh + * Threshold + */ +void Adafruit_APDS9960::setGestureProximityThreshold(uint8_t thresh) { + this->write8(APDS9960_GPENTH, thresh); +} + +/*! + * @brief Sets gesture sensor offset + * @param offset_up + * Up offset + * @param offset_down + * Down offset + * @param offset_left + * Left offset + * @param offset_right + * Right offset + */ +void Adafruit_APDS9960::setGestureOffset(uint8_t offset_up, uint8_t offset_down, + uint8_t offset_left, + uint8_t offset_right) { + this->write8(APDS9960_GOFFSET_U, offset_up); + this->write8(APDS9960_GOFFSET_D, offset_down); + this->write8(APDS9960_GOFFSET_L, offset_left); + this->write8(APDS9960_GOFFSET_R, offset_right); +} + +/*! + * @brief Enable gesture readings on APDS9960 + * @param en + * Enable (True/False) + */ +void Adafruit_APDS9960::enableGesture(boolean en) { + if (!en) { + _gconf4.GMODE = 0; + write8(APDS9960_GCONF4, _gconf4.get()); } _enable.GEN = en; write8(APDS9960_ENABLE, _enable.get()); resetCounts(); } -void Adafruit_APDS9960::resetCounts() -{ +/*! + * @brief Resets gesture counts + */ +void Adafruit_APDS9960::resetCounts() { gestCnt = 0; UCount = 0; DCount = 0; @@ -316,125 +385,144 @@ void Adafruit_APDS9960::resetCounts() RCount = 0; } -uint8_t Adafruit_APDS9960::readGesture(void) -{ - uint8_t toRead, bytesRead; - uint8_t buf[256]; - unsigned long t; - uint8_t gestureReceived; - while(1){ - int up_down_diff = 0; - int left_right_diff = 0; - gestureReceived = 0; - if(!gestureValid()) return 0; - - delay(30); - toRead = this->read8(APDS9960_GFLVL); - - bytesRead = this->read(APDS9960_GFIFO_U, buf, toRead); +/*! + * @brief Reads gesture + * @return Received gesture (APDS9960_DOWN APDS9960_UP, APDS9960_LEFT + * APDS9960_RIGHT) + */ +uint8_t Adafruit_APDS9960::readGesture() { + uint8_t toRead, bytesRead; + uint8_t buf[256]; + unsigned long t = 0; + uint8_t gestureReceived; + while (1) { + int up_down_diff = 0; + int left_right_diff = 0; + gestureReceived = 0; + if (!gestureValid()) + return 0; - if(abs((int)buf[0] - (int)buf[1]) > 13) + delay(30); + toRead = this->read8(APDS9960_GFLVL); + + // bytesRead is unused but produces sideffects needed for readGesture to work + bytesRead = this->read(APDS9960_GFIFO_U, buf, toRead); + + if (abs((int)buf[0] - (int)buf[1]) > 13) up_down_diff += (int)buf[0] - (int)buf[1]; - if(abs((int)buf[2] - (int)buf[3]) > 13) + if (abs((int)buf[2] - (int)buf[3]) > 13) left_right_diff += (int)buf[2] - (int)buf[3]; - if(up_down_diff != 0){ - if(up_down_diff < 0){ - if( DCount > 0){ - gestureReceived = APDS9960_UP; - } - else UCount++; - } - else if(up_down_diff > 0){ - if( UCount > 0){ - gestureReceived = APDS9960_DOWN; - } - else DCount++; - } - } - - if(left_right_diff != 0){ - if(left_right_diff < 0){ - if( RCount > 0){ - gestureReceived = APDS9960_LEFT; - } - else LCount++; - } - else if(left_right_diff > 0){ - if( LCount > 0){ - gestureReceived = APDS9960_RIGHT; - } - else RCount++; - } - } - - if(up_down_diff != 0 || left_right_diff != 0) t = millis(); - - if(gestureReceived || millis() - t > 300){ - resetCounts(); - return gestureReceived; - } - } + if (up_down_diff != 0) { + if (up_down_diff < 0) { + if (DCount > 0) { + gestureReceived = APDS9960_UP; + } else + UCount++; + } else if (up_down_diff > 0) { + if (UCount > 0) { + gestureReceived = APDS9960_DOWN; + } else + DCount++; + } + } + + if (left_right_diff != 0) { + if (left_right_diff < 0) { + if (RCount > 0) { + gestureReceived = APDS9960_LEFT; + } else + LCount++; + } else if (left_right_diff > 0) { + if (LCount > 0) { + gestureReceived = APDS9960_RIGHT; + } else + RCount++; + } + } + + if (up_down_diff != 0 || left_right_diff != 0) + t = millis(); + + if (gestureReceived || millis() - t > 300) { + resetCounts(); + return gestureReceived; + } + } } -/**************************************************************************/ /*! - Set LED brightness for proximity/gesture -*/ -/**************************************************************************/ -void Adafruit_APDS9960::setLED(apds9960LedDrive_t drive, apds9960LedBoost_t boost) { + * @brief Set LED brightness for proximity/gesture + * @param drive + * LED Drive + * @param boost + * LED Boost + */ +void Adafruit_APDS9960::setLED(apds9960LedDrive_t drive, + apds9960LedBoost_t boost) { // set BOOST _config2.LED_BOOST = boost; write8(APDS9960_CONFIG2, _config2.get()); - + _control.LDRIVE = drive; write8(APDS9960_CONTROL, _control.get()); } -/**************************************************************************/ /*! - Enable proximity readings on APDS9960 -*/ -/**************************************************************************/ -void Adafruit_APDS9960::enableColor(boolean en) -{ + * @brief Enable proximity readings on APDS9960 + * @param en + * Enable (True/False) + */ +void Adafruit_APDS9960::enableColor(boolean en) { _enable.AEN = en; write8(APDS9960_ENABLE, _enable.get()); } -bool Adafruit_APDS9960::colorDataReady() -{ - _status.set(this->read8(APDS9960_STATUS)); - return _status.AVALID; +/*! + * @brief Returns status of color data + * @return True if color data ready, False otherwise + */ +bool Adafruit_APDS9960::colorDataReady() { + _status.set(this->read8(APDS9960_STATUS)); + return _status.AVALID; } -/**************************************************************************/ /*! - @brief Reads the raw red, green, blue and clear channel values -*/ -/**************************************************************************/ -void Adafruit_APDS9960::getColorData (uint16_t *r, uint16_t *g, uint16_t *b, uint16_t *c) -{ - + * @brief Reads the raw red, green, blue and clear channel values + * @param *r + * Red value + * @param *g + * Green value + * @param *b + * Blue value + * @param *c + * Clear channel value + */ +void Adafruit_APDS9960::getColorData(uint16_t *r, uint16_t *g, uint16_t *b, + uint16_t *c) { + *c = read16R(APDS9960_CDATAL); *r = read16R(APDS9960_RDATAL); *g = read16R(APDS9960_GDATAL); *b = read16R(APDS9960_BDATAL); - } -/**************************************************************************/ /*! - @brief Converts the raw R/G/B values to color temperature in degrees - Kelvin -*/ -/**************************************************************************/ -uint16_t Adafruit_APDS9960::calculateColorTemperature(uint16_t r, uint16_t g, uint16_t b) -{ - float X, Y, Z; /* RGB to XYZ correlation */ - float xc, yc; /* Chromaticity co-ordinates */ - float n; /* McCamy's formula */ + * @brief Converts the raw R/G/B values to color temperature in degrees Kelvin + * @param r + * Red value + * @param g + * Green value + * @param b + * Blue value + * @return Color temperature + */ +uint16_t Adafruit_APDS9960::calculateColorTemperature(uint16_t r, uint16_t g, + uint16_t b) { + 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. */ @@ -443,7 +531,7 @@ uint16_t Adafruit_APDS9960::calculateColorTemperature(uint16_t r, uint16_t g, ui /* Note: Y = Illuminance or lux */ X = (-0.14282F * r) + (1.54924F * g) + (-0.95641F * b); Y = (-0.32466F * r) + (1.57837F * g) + (-0.73191F * b); - Z = (-0.68202F * r) + (0.77073F * g) + ( 0.56332F * b); + Z = (-0.68202F * r) + (0.77073F * g) + (0.56332F * b); /* 2. Calculate the chromaticity co-ordinates */ xc = (X) / (X + Y + Z); @@ -453,20 +541,24 @@ uint16_t Adafruit_APDS9960::calculateColorTemperature(uint16_t r, uint16_t g, ui n = (xc - 0.3320F) / (0.1858F - yc); /* Calculate the final CCT */ - cct = (449.0F * powf(n, 3)) + (3525.0F * powf(n, 2)) + (6823.3F * n) + 5520.33F; + cct = + (449.0F * powf(n, 3)) + (3525.0F * powf(n, 2)) + (6823.3F * n) + 5520.33F; /* Return the results in degrees Kelvin */ return (uint16_t)cct; } -/**************************************************************************/ /*! - @brief Calculate ambient light values -*/ -/**************************************************************************/ - -uint16_t Adafruit_APDS9960::calculateLux(uint16_t r, uint16_t g, uint16_t b) -{ + * @brief Calculate ambient light values + * @param r + * Red value + * @param g + * Green value + * @param b + * Blue value + * @return LUX value + */ +uint16_t Adafruit_APDS9960::calculateLux(uint16_t r, uint16_t g, uint16_t b) { float illuminance; /* This only uses RGB ... how can we integrate clear or calculate lux */ @@ -476,102 +568,159 @@ uint16_t Adafruit_APDS9960::calculateLux(uint16_t r, uint16_t g, uint16_t b) return (uint16_t)illuminance; } +/*! + * @brief Enables color interrupt + */ void Adafruit_APDS9960::enableColorInterrupt() { _enable.AIEN = 1; write8(APDS9960_ENABLE, _enable.get()); } +/*! + * @brief Disables color interrupt + */ void Adafruit_APDS9960::disableColorInterrupt() { - _enable.AIEN = 0; - write8(APDS9960_ENABLE, _enable.get()); + _enable.AIEN = 0; + write8(APDS9960_ENABLE, _enable.get()); } -void Adafruit_APDS9960::clearInterrupt(void) { +/*! + * @brief Clears interrupt + */ +void Adafruit_APDS9960::clearInterrupt() { this->write(APDS9960_AICLEAR, NULL, 0); } - +/*! + * @brief Sets interrupt limits + * @param low + * Low limit + * @param high + * High limit + */ void Adafruit_APDS9960::setIntLimits(uint16_t low, uint16_t high) { - write8(APDS9960_AILTIL, low & 0xFF); - write8(APDS9960_AILTH, low >> 8); - write8(APDS9960_AIHTL, high & 0xFF); - write8(APDS9960_AIHTH, high >> 8); + write8(APDS9960_AILTIL, low & 0xFF); + write8(APDS9960_AILTH, low >> 8); + write8(APDS9960_AIHTL, high & 0xFF); + write8(APDS9960_AIHTH, high >> 8); } -void Adafruit_APDS9960::write8(byte reg, byte value) -{ - this->write(reg, &value, 1); +/*! + * @brief Writes specified value to given register + * @param reg + * Register to write to + * @param value + * Value to write + */ +void Adafruit_APDS9960::write8(byte reg, byte value) { + this->write(reg, &value, 1); } -uint8_t Adafruit_APDS9960::read8(byte reg) -{ - uint8_t ret; - this->read(reg, &ret, 1); - - return ret; +/*! + * @brief Reads 8 bits from specified register + * @param reg + * Register to write to + * @return Value in register + */ +uint8_t Adafruit_APDS9960::read8(byte reg) { + uint8_t ret; + this->read(reg, &ret, 1); + + return ret; } -uint32_t Adafruit_APDS9960::read32(uint8_t reg) -{ - uint8_t ret[4]; - this->read(reg, ret, 4); - - return (ret[0] << 24) | (ret[1] << 16) | (ret[2] << 8) | ret[3]; +/*! + * @brief Reads 32 bits from specified register + * @param reg + * Register to write to + * @return Value in register + */ +uint32_t Adafruit_APDS9960::read32(uint8_t reg) { + uint8_t ret[4]; + this->read(reg, ret, 4); + + return (ret[0] << 24) | (ret[1] << 16) | (ret[2] << 8) | ret[3]; } -uint16_t Adafruit_APDS9960::read16(uint8_t reg) -{ - uint8_t ret[2]; - this->read(reg, ret, 2); - - return (ret[0] << 8) | ret[1]; -} - -uint16_t Adafruit_APDS9960::read16R(uint8_t reg) -{ +/*! + * @brief Reads 16 bites from specified register + * @param reg + * Register to write to + * @return Value in register + */ +uint16_t Adafruit_APDS9960::read16(uint8_t reg) { uint8_t ret[2]; this->read(reg, ret, 2); - + + return (ret[0] << 8) | ret[1]; +} + +/*! + * @brief Reads 16 bites from specified register + * @param reg + * Register to write to + * @return Value in register + */ +uint16_t Adafruit_APDS9960::read16R(uint8_t reg) { + uint8_t ret[2]; + this->read(reg, ret, 2); + return (ret[1] << 8) | ret[0]; } -void Adafruit_APDS9960::_i2c_init() -{ - Wire.begin(); +/*! + * @brief Begins I2C communication + */ +void Adafruit_APDS9960::_i2c_init() { _wire->begin(); } + +/*! + * @brief Reads num bytes from specified register into a given buffer + * @param reg + * Register + * @param *buf + * Buffer + * @param num + * Number of bytes + * @return Position after reading + */ +uint8_t Adafruit_APDS9960::read(uint8_t reg, uint8_t *buf, uint8_t num) { + uint8_t pos = 0; + bool eof = false; + + // on arduino we need to read in 32 byte chunks + while (pos < num && !eof) { + + uint8_t read_now = min(32, num - pos); + _wire->beginTransmission((uint8_t)_i2caddr); + _wire->write((uint8_t)reg + pos); + _wire->endTransmission(); + + _wire->requestFrom((uint8_t)_i2caddr, read_now); + + for (int i = 0; i < read_now; i++) { + if (!_wire->available()) { + eof = true; + break; + } + buf[pos] = _wire->read(); + pos++; + } + } + return pos; } -uint8_t Adafruit_APDS9960::read(uint8_t reg, uint8_t *buf, uint8_t num) -{ - uint8_t value; - uint8_t pos = 0; - bool eof = false; - - //on arduino we need to read in 32 byte chunks - while(pos < num && !eof){ - - uint8_t read_now = min(32, num - pos); - Wire.beginTransmission((uint8_t)_i2caddr); - Wire.write((uint8_t)reg + pos); - Wire.endTransmission(); - - Wire.requestFrom((uint8_t)_i2caddr, read_now); - - for(int i=0; ibeginTransmission((uint8_t)_i2caddr); + _wire->write((uint8_t)reg); + _wire->write((uint8_t *)buf, num); + _wire->endTransmission(); } diff --git a/firmware/libraries/Adafruit_APDS9960_Library/Adafruit_APDS9960.h b/firmware/libraries/Adafruit_APDS9960_Library/Adafruit_APDS9960.h index 177a202..1ba957d 100644 --- a/firmware/libraries/Adafruit_APDS9960_Library/Adafruit_APDS9960.h +++ b/firmware/libraries/Adafruit_APDS9960_Library/Adafruit_APDS9960.h @@ -1,558 +1,531 @@ -/**************************************************************************/ -/*! - @file Adafruit_APDS9960.h - @author Ladyada, Dean Miller (Adafruit Industries) - - @section LICENSE - - Software License Agreement (BSD License) - - Copyright (c) 2017, Adafruit Industries - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. 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. - 3. Neither the name of the copyright holders nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 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. -*/ -/**************************************************************************/ +/*! + * @file Adafruit_APDS9960.h + * + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Adafruit Industries + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holders nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 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. + */ #ifndef _APDS9960_H_ #define _APDS9960_H_ #include #include -#define I2CDEBUG +#define APDS9960_ADDRESS (0x39) /**< I2C Address */ -/*========================================================================= - I2C ADDRESS/BITS - -----------------------------------------------------------------------*/ - #define APDS9960_ADDRESS (0x39) -/*=========================================================================*/ - -/*========================================================================= - REGISTERS - -----------------------------------------------------------------------*/ - - enum - { - APDS9960_RAM = 0x00, - APDS9960_ENABLE = 0x80, - APDS9960_ATIME = 0x81, - APDS9960_WTIME = 0x83, - APDS9960_AILTIL = 0x84, - APDS9960_AILTH = 0x85, - APDS9960_AIHTL = 0x86, - APDS9960_AIHTH = 0x87, - APDS9960_PILT = 0x89, - APDS9960_PIHT = 0x8B, - APDS9960_PERS = 0x8C, - APDS9960_CONFIG1 = 0x8D, - APDS9960_PPULSE = 0x8E, - APDS9960_CONTROL = 0x8F, - APDS9960_CONFIG2 = 0x90, - APDS9960_ID = 0x92, - APDS9960_STATUS = 0x93, - APDS9960_CDATAL = 0x94, - APDS9960_CDATAH = 0x95, - APDS9960_RDATAL = 0x96, - APDS9960_RDATAH = 0x97, - APDS9960_GDATAL = 0x98, - APDS9960_GDATAH = 0x99, - APDS9960_BDATAL = 0x9A, - APDS9960_BDATAH = 0x9B, - APDS9960_PDATA = 0x9C, - APDS9960_POFFSET_UR = 0x9D, - APDS9960_POFFSET_DL = 0x9E, - APDS9960_CONFIG3 = 0x9F, - APDS9960_GPENTH = 0xA0, - APDS9960_GEXTH = 0xA1, - APDS9960_GCONF1 = 0xA2, - APDS9960_GCONF2 = 0xA3, - APDS9960_GOFFSET_U = 0xA4, - APDS9960_GOFFSET_D = 0xA5, - APDS9960_GOFFSET_L = 0xA7, - APDS9960_GOFFSET_R = 0xA9, - APDS9960_GPULSE = 0xA6, - APDS9960_GCONF3 = 0xAA, - APDS9960_GCONF4 = 0xAB, - APDS9960_GFLVL = 0xAE, - APDS9960_GSTATUS = 0xAF, - APDS9960_IFORCE = 0xE4, - APDS9960_PICLEAR = 0xE5, - APDS9960_CICLEAR = 0xE6, - APDS9960_AICLEAR = 0xE7, - APDS9960_GFIFO_U = 0xFC, - APDS9960_GFIFO_D = 0xFD, - APDS9960_GFIFO_L = 0xFE, - APDS9960_GFIFO_R = 0xFF, - }; - - -/*=========================================================================*/ - -typedef enum -{ - APDS9960_AGAIN_1X = 0x00, /**< No gain */ - APDS9960_AGAIN_4X = 0x01, /**< 2x gain */ - APDS9960_AGAIN_16X = 0x02, /**< 16x gain */ - APDS9960_AGAIN_64X = 0x03 /**< 64x gain */ -} -apds9960AGain_t; - - -typedef enum -{ - APDS9960_PGAIN_1X = 0x00, /**< 1x gain */ - APDS9960_PGAIN_2X = 0x04, /**< 2x gain */ - APDS9960_PGAIN_4X = 0x08, /**< 4x gain */ - APDS9960_PGAIN_8X = 0x0C /**< 8x gain */ -} -apds9960PGain_t; - - -typedef enum -{ - APDS9960_PPULSELEN_4US = 0x00, /**< 4uS */ - APDS9960_PPULSELEN_8US = 0x40, /**< 8uS */ - APDS9960_PPULSELEN_16US = 0x80, /**< 16uS */ - APDS9960_PPULSELEN_32US = 0xC0 /**< 32uS */ -} -apds9960PPulseLen_t; - - -typedef enum -{ - APDS9960_LEDDRIVE_100MA = 0x00, /**< 100mA */ - APDS9960_LEDDRIVE_50MA = 0x40, /**< 50mA */ - APDS9960_LEDDRIVE_25MA = 0x80, /**< 25mA */ - APDS9960_LEDDRIVE_12MA = 0xC0 /**< 12.5mA */ -} -apds9960LedDrive_t; - - -typedef enum -{ - APDS9960_LEDBOOST_100PCNT = 0x00, /**< 100% */ - APDS9960_LEDBOOST_150PCNT = 0x10, /**< 150% */ - APDS9960_LEDBOOST_200PCNT = 0x20, /**< 200% */ - APDS9960_LEDBOOST_300PCNT = 0x30 /**< 300% */ -} -apds9960LedBoost_t; - -enum -{ - APDS9960_DIMENSIONS_ALL = 0x00, - APDS9960_DIMENSIONS_UP_DOWM = 0x01, - APGS9960_DIMENSIONS_LEFT_RIGHT = 0x02, +/** I2C Registers */ +enum { + APDS9960_RAM = 0x00, + APDS9960_ENABLE = 0x80, + APDS9960_ATIME = 0x81, + APDS9960_WTIME = 0x83, + APDS9960_AILTIL = 0x84, + APDS9960_AILTH = 0x85, + APDS9960_AIHTL = 0x86, + APDS9960_AIHTH = 0x87, + APDS9960_PILT = 0x89, + APDS9960_PIHT = 0x8B, + APDS9960_PERS = 0x8C, + APDS9960_CONFIG1 = 0x8D, + APDS9960_PPULSE = 0x8E, + APDS9960_CONTROL = 0x8F, + APDS9960_CONFIG2 = 0x90, + APDS9960_ID = 0x92, + APDS9960_STATUS = 0x93, + APDS9960_CDATAL = 0x94, + APDS9960_CDATAH = 0x95, + APDS9960_RDATAL = 0x96, + APDS9960_RDATAH = 0x97, + APDS9960_GDATAL = 0x98, + APDS9960_GDATAH = 0x99, + APDS9960_BDATAL = 0x9A, + APDS9960_BDATAH = 0x9B, + APDS9960_PDATA = 0x9C, + APDS9960_POFFSET_UR = 0x9D, + APDS9960_POFFSET_DL = 0x9E, + APDS9960_CONFIG3 = 0x9F, + APDS9960_GPENTH = 0xA0, + APDS9960_GEXTH = 0xA1, + APDS9960_GCONF1 = 0xA2, + APDS9960_GCONF2 = 0xA3, + APDS9960_GOFFSET_U = 0xA4, + APDS9960_GOFFSET_D = 0xA5, + APDS9960_GOFFSET_L = 0xA7, + APDS9960_GOFFSET_R = 0xA9, + APDS9960_GPULSE = 0xA6, + APDS9960_GCONF3 = 0xAA, + APDS9960_GCONF4 = 0xAB, + APDS9960_GFLVL = 0xAE, + APDS9960_GSTATUS = 0xAF, + APDS9960_IFORCE = 0xE4, + APDS9960_PICLEAR = 0xE5, + APDS9960_CICLEAR = 0xE6, + APDS9960_AICLEAR = 0xE7, + APDS9960_GFIFO_U = 0xFC, + APDS9960_GFIFO_D = 0xFD, + APDS9960_GFIFO_L = 0xFE, + APDS9960_GFIFO_R = 0xFF, }; -enum -{ - APDS9960_GFIFO_1 = 0x00, - APDS9960_GFIFO_4 = 0x01, - APDS9960_GFIFO_8 = 0x02, - APDS9960_GFIFO_16 = 0x03, +/** ADC gain settings */ +typedef enum { + APDS9960_AGAIN_1X = 0x00, /**< No gain */ + APDS9960_AGAIN_4X = 0x01, /**< 2x gain */ + APDS9960_AGAIN_16X = 0x02, /**< 16x gain */ + APDS9960_AGAIN_64X = 0x03 /**< 64x gain */ +} apds9960AGain_t; + +/** Proxmity gain settings */ +typedef enum { + APDS9960_PGAIN_1X = 0x00, /**< 1x gain */ + APDS9960_PGAIN_2X = 0x04, /**< 2x gain */ + APDS9960_PGAIN_4X = 0x08, /**< 4x gain */ + APDS9960_PGAIN_8X = 0x0C /**< 8x gain */ +} apds9960PGain_t; + +/** Pulse length settings */ +typedef enum { + APDS9960_PPULSELEN_4US = 0x00, /**< 4uS */ + APDS9960_PPULSELEN_8US = 0x40, /**< 8uS */ + APDS9960_PPULSELEN_16US = 0x80, /**< 16uS */ + APDS9960_PPULSELEN_32US = 0xC0 /**< 32uS */ +} apds9960PPulseLen_t; + +/** LED drive settings */ +typedef enum { + APDS9960_LEDDRIVE_100MA = 0x00, /**< 100mA */ + APDS9960_LEDDRIVE_50MA = 0x40, /**< 50mA */ + APDS9960_LEDDRIVE_25MA = 0x80, /**< 25mA */ + APDS9960_LEDDRIVE_12MA = 0xC0 /**< 12.5mA */ +} apds9960LedDrive_t; + +/** LED boost settings */ +typedef enum { + APDS9960_LEDBOOST_100PCNT = 0x00, /**< 100% */ + APDS9960_LEDBOOST_150PCNT = 0x10, /**< 150% */ + APDS9960_LEDBOOST_200PCNT = 0x20, /**< 200% */ + APDS9960_LEDBOOST_300PCNT = 0x30 /**< 300% */ +} apds9960LedBoost_t; + +/** Dimensions */ +enum { + APDS9960_DIMENSIONS_ALL = 0x00, // All dimensions + APDS9960_DIMENSIONS_UP_DOWN = 0x01, // Up/Down dimensions + APGS9960_DIMENSIONS_LEFT_RIGHT = 0x02, // Left/Right dimensions }; -enum -{ - APDS9960_GGAIN_1 = 0x00, - APDS9960_GGAIN_2 = 0x01, - APDS9960_GGAIN_4 = 0x02, - APDS9960_GGAIN_8 = 0x03, +/** FIFO Interrupts */ +enum { + APDS9960_GFIFO_1 = 0x00, // Generate interrupt after 1 dataset in FIFO + APDS9960_GFIFO_4 = 0x01, // Generate interrupt after 2 datasets in FIFO + APDS9960_GFIFO_8 = 0x02, // Generate interrupt after 3 datasets in FIFO + APDS9960_GFIFO_16 = 0x03, // Generate interrupt after 4 datasets in FIFO }; -enum -{ - APDS9960_GPULSE_4US = 0x00, - APDS9960_GPULSE_8US = 0x01, - APDS9960_GPULSE_16US = 0x02, - APDS9960_GPULSE_32US = 0x03, +/** Gesture Gain */ +enum { + APDS9960_GGAIN_1 = 0x00, // Gain 1x + APDS9960_GGAIN_2 = 0x01, // Gain 2x + APDS9960_GGAIN_4 = 0x02, // Gain 4x + APDS9960_GGAIN_8 = 0x03, // Gain 8x }; -#define APDS9960_TIME_MULT 2.78 //millisec +/** Pulse Lenghts */ +enum { + APDS9960_GPULSE_4US = 0x00, // Pulse 4us + APDS9960_GPULSE_8US = 0x01, // Pulse 8us + APDS9960_GPULSE_16US = 0x02, // Pulse 16us + APDS9960_GPULSE_32US = 0x03, // Pulse 32us +}; -#define APDS9960_UP 0x01 -#define APDS9960_DOWN 0x02 -#define APDS9960_LEFT 0x03 -#define APDS9960_RIGHT 0x04 +#define APDS9960_UP 0x01 /**< Gesture Up */ +#define APDS9960_DOWN 0x02 /**< Gesture Down */ +#define APDS9960_LEFT 0x03 /**< Gesture Left */ +#define APDS9960_RIGHT 0x04 /**< Gesture Right */ +/*! + * @brief Class that stores state and functions for interacting with + * APDS9960 Sensor + */ class Adafruit_APDS9960 { - public: - Adafruit_APDS9960(void) {}; - ~Adafruit_APDS9960(void) {}; - - boolean begin(uint16_t iTimeMS = 10, apds9960AGain_t = APDS9960_AGAIN_4X, uint8_t addr = APDS9960_ADDRESS); - void setADCIntegrationTime(uint16_t iTimeMS); - float getADCIntegrationTime(void); - void setADCGain(apds9960AGain_t gain); - apds9960AGain_t getADCGain(void); - void setLED(apds9960LedDrive_t drive, apds9960LedBoost_t boost); +public: + Adafruit_APDS9960(){}; + ~Adafruit_APDS9960(){}; + + boolean begin(uint16_t iTimeMS = 10, apds9960AGain_t = APDS9960_AGAIN_4X, + uint8_t addr = APDS9960_ADDRESS, TwoWire *theWire = &Wire); + void setADCIntegrationTime(uint16_t iTimeMS); + float getADCIntegrationTime(); + void setADCGain(apds9960AGain_t gain); + apds9960AGain_t getADCGain(); + void setLED(apds9960LedDrive_t drive, apds9960LedBoost_t boost); // proximity - void enableProximity(boolean en = true); - void setProxGain(apds9960PGain_t gain); - apds9960PGain_t getProxGain(void); - void setProxPulse(apds9960PPulseLen_t pLen, uint8_t pulses); - void enableProximityInterrupt(); - void disableProximityInterrupt(); - uint8_t readProximity(void); - void setProximityInterruptThreshold(uint8_t low, uint8_t high, uint8_t persistance = 4); - bool getProximityInterrupt(); + void enableProximity(boolean en = true); + void setProxGain(apds9960PGain_t gain); + apds9960PGain_t getProxGain(); + void setProxPulse(apds9960PPulseLen_t pLen, uint8_t pulses); + void enableProximityInterrupt(); + void disableProximityInterrupt(); + uint8_t readProximity(); + void setProximityInterruptThreshold(uint8_t low, uint8_t high, + uint8_t persistance = 4); + bool getProximityInterrupt(); // gesture - void enableGesture(boolean en = true); - bool gestureValid(); - void setGestureDimensions(uint8_t dims); - void setGestureFIFOThreshold(uint8_t thresh); - void setGestureGain(uint8_t gain); - void setGestureProximityThreshold(uint8_t thresh); - void setGestureOffset(uint8_t offset_up, uint8_t offset_down, uint8_t offset_left, uint8_t offset_right); - uint8_t readGesture(void); - void resetCounts(); + void enableGesture(boolean en = true); + bool gestureValid(); + void setGestureDimensions(uint8_t dims); + void setGestureFIFOThreshold(uint8_t thresh); + void setGestureGain(uint8_t gain); + void setGestureProximityThreshold(uint8_t thresh); + void setGestureOffset(uint8_t offset_up, uint8_t offset_down, + uint8_t offset_left, uint8_t offset_right); + uint8_t readGesture(); + void resetCounts(); // light & color - void enableColor(boolean en = true); - bool colorDataReady(); - void getColorData(uint16_t *r, uint16_t *g, uint16_t *b, uint16_t *c); + void enableColor(boolean en = true); + bool colorDataReady(); + void getColorData(uint16_t *r, uint16_t *g, uint16_t *b, uint16_t *c); uint16_t calculateColorTemperature(uint16_t r, uint16_t g, uint16_t b); uint16_t calculateLux(uint16_t r, uint16_t g, uint16_t b); void enableColorInterrupt(); void disableColorInterrupt(); - void clearInterrupt(void); + void clearInterrupt(); void setIntLimits(uint16_t l, uint16_t h); // turn on/off elements - void enable(boolean en = true); + void enable(boolean en = true); - - private: +private: uint8_t _i2caddr; - + TwoWire *_wire; + uint32_t read32(uint8_t reg); uint16_t read16(uint8_t reg); uint16_t read16R(uint8_t reg); - - void write8(byte reg, byte value); - uint8_t read8(byte reg); - + + void write8(byte reg, byte value); + uint8_t read8(byte reg); + uint8_t gestCnt; - + uint8_t UCount; uint8_t DCount; - + uint8_t LCount; uint8_t RCount; - + uint8_t read(uint8_t reg, uint8_t *buf, uint8_t num); void write(uint8_t reg, uint8_t *buf, uint8_t num); void _i2c_init(); - + struct enable { - - //power on - uint8_t PON : 1; - - //ALS enable - uint8_t AEN : 1; - - //Proximity detect enable - uint8_t PEN : 1; - - //wait timer enable - uint8_t WEN : 1; - - //ALS interrupt enable - uint8_t AIEN : 1; - - //proximity interrupt enable - uint8_t PIEN : 1; - - //gesture enable - uint8_t GEN : 1; - - uint8_t get() { - return (GEN << 6) | (PIEN << 5) | (AIEN << 4) | (WEN << 3) | (PEN << 2) | (AEN << 1) | PON; - }; - }; - struct enable _enable; - - struct pers { - //ALS Interrupt Persistence. Controls rate of Clear channel interrupt to the host processor - uint8_t APERS : 4; - - //proximity interrupt persistence, controls rate of prox interrupt to host processor - uint8_t PPERS : 4; - - uint8_t get(){ - return (PPERS << 4) | APERS; - }; - }; - pers _pers; - - struct config1 { - uint8_t WLONG : 1; - - uint8_t get(){ - return WLONG << 1; - }; - }; - config1 _config1; - - struct ppulse { - - /*Proximity Pulse Count. Specifies the number of proximity pulses to be generated on LDR. - Number of pulses is set by PPULSE value plus 1. - */ - uint8_t PPULSE : 6; - - //Proximity Pulse Length. Sets the LED-ON pulse width during a proximity LDR pulse. - uint8_t PPLEN : 2; - - uint8_t get(){ - return (PPLEN << 6) | PPULSE; - } - }; - ppulse _ppulse; - - struct control { - //ALS and Color gain control - uint8_t AGAIN : 2; - - //proximity gain control - uint8_t PGAIN : 2; - - //led drive strength - uint8_t LDRIVE : 2; - - uint8_t get(){ - return (LDRIVE << 6) | (PGAIN << 2) | AGAIN; - } - }; - control _control; - - struct config2 { - /* Additional LDR current during proximity and gesture LED pulses. Current value, set by LDRIVE, - is increased by the percentage of LED_BOOST. - */ - uint8_t LED_BOOST : 2; - - //clear photodiode saturation int enable - uint8_t CPSIEN : 1; - - //proximity saturation interrupt enable - uint8_t PSIEN : 1; - - uint8_t get(){ - return (PSIEN << 7) | (CPSIEN << 6) | (LED_BOOST << 4) | 1; - } - }; - config2 _config2; - - struct status { - /* ALS Valid. Indicates that an ALS cycle has completed since AEN was asserted or since a read - from any of the ALS/Color data registers. - */ - uint8_t AVALID : 1; - - /* Proximity Valid. Indicates that a proximity cycle has completed since PEN was asserted or since - PDATA was last read. A read of PDATA automatically clears PVALID. - */ - uint8_t PVALID : 1; - - /* Gesture Interrupt. GINT is asserted when GFVLV becomes greater than GFIFOTH or if GVALID - has become asserted when GMODE transitioned to zero. The bit is reset when FIFO is - completely emptied (read). - */ - uint8_t GINT : 1; - - //ALS Interrupt. This bit triggers an interrupt if AIEN in ENABLE is set. - uint8_t AINT : 1; - - //Proximity Interrupt. This bit triggers an interrupt if PIEN in ENABLE is set. - uint8_t PINT : 1; - - /* Indicates that an analog saturation event occurred during a previous proximity or gesture - cycle. Once set, this bit remains set until cleared by clear proximity interrupt special function - command (0xE5 PICLEAR) or by disabling Prox (PEN=0). This bit triggers an interrupt if PSIEN - is set. - */ - uint8_t PGSAT : 1; - - /* Clear Photodiode Saturation. When asserted, the analog sensor was at the upper end of its - dynamic range. The bit can be de-asserted by sending a Clear channel interrupt command - (0xE6 CICLEAR) or by disabling the ADC (AEN=0). This bit triggers an interrupt if CPSIEN is set. - */ - uint8_t CPSAT : 1; - - void set(uint8_t data){ - AVALID = data & 0x01; - PVALID = (data >> 1) & 0x01; - GINT = (data >> 2) & 0x01; - AINT = (data >> 4) & 0x01; - PINT = (data >> 5) & 0x01; - PGSAT = (data >> 6) & 0x01; - CPSAT = (data >> 7) & 0x01; - } - }; - status _status; - - struct config3 { - //proximity mask - uint8_t PMASK_R : 1; - uint8_t PMASK_L : 1; - uint8_t PMASK_D : 1; - uint8_t PMASK_U : 1; - - /* Sleep After Interrupt. When enabled, the device will automatically enter low power mode - when the INT pin is asserted and the state machine has progressed to the SAI decision block. - Normal operation is resumed when INT pin is cleared over I2C. - */ - uint8_t SAI : 1; - - /* Proximity Gain Compensation Enable. This bit provides gain compensation when proximity - photodiode signal is reduced as a result of sensor masking. If only one diode of the diode pair - is contributing, then only half of the signal is available at the ADC; this results in a maximum - ADC value of 127. Enabling PCMP enables an additional gain of 2X, resulting in a maximum - ADC value of 255. - */ - uint8_t PCMP : 1; - - uint8_t get(){ - return (PCMP << 5) | (SAI << 4) | (PMASK_U << 3) | (PMASK_D << 2) | (PMASK_L << 1) | PMASK_R; - } - }; - config3 _config3; - - struct gconf1 { - /* Gesture Exit Persistence. When a number of consecutive “gesture end” occurrences become - equal or greater to the GEPERS value, the Gesture state machine is exited. - */ - uint8_t GEXPERS : 2; - - /* Gesture Exit Mask. Controls which of the gesture detector photodiodes (UDLR) will be included - to determine a “gesture end” and subsequent exit of the gesture state machine. Unmasked - UDLR data will be compared with the value in GTHR_OUT. Field value bits correspond to UDLR - detectors. - */ - uint8_t GEXMSK : 4; - - /* Gesture FIFO Threshold. This value is compared with the FIFO Level (i.e. the number of UDLR - datasets) to generate an interrupt (if enabled). - */ - uint8_t GFIFOTH : 2; - - uint8_t get(){ - return (GFIFOTH << 6) | (GEXMSK << 2) | GEXPERS; - } - }; - gconf1 _gconf1; - - struct gconf2 { - /* Gesture Wait Time. The GWTIME controls the amount of time in a low power mode between - gesture detection cycles. - */ - uint8_t GWTIME : 3; - - //Gesture LED Drive Strength. Sets LED Drive Strength in gesture mode. - uint8_t GLDRIVE : 2; - - //Gesture Gain Control. Sets the gain of the proximity receiver in gesture mode. - uint8_t GGAIN : 2; - - uint8_t get(){ - return (GGAIN << 5) | (GLDRIVE << 3) | GWTIME; - } - }; - gconf2 _gconf2; - - struct gpulse { - /* Number of Gesture Pulses. Specifies the number of pulses to be generated on LDR. - Number of pulses is set by GPULSE value plus 1. - */ - uint8_t GPULSE : 6; - - //Gesture Pulse Length. Sets the LED_ON pulse width during a Gesture LDR Pulse. - uint8_t GPLEN : 2; - - uint8_t get(){ - return (GPLEN << 6) | GPULSE; - } - }; - gpulse _gpulse; - - struct gconf3 { - /* Gesture Dimension Select. Selects which gesture photodiode pairs are enabled to gather - results during gesture. - */ - uint8_t GDIMS : 2; - - uint8_t get(){ - return GDIMS; - } - }; - gconf3 _gconf3; - - struct gconf4 { - /* Gesture Mode. Reading this bit reports if the gesture state machine is actively running, 1 - = Gesture, 0= ALS, Proximity, Color. Writing a 1 to this bit causes immediate entry in to the - gesture state machine (as if GPENTH had been exceeded). Writing a 0 to this bit causes exit of - gesture when current analog conversion has finished (as if GEXTH had been exceeded). - */ - uint8_t GMODE : 1; - - /* Gesture interrupt enable. Gesture Interrupt Enable. When asserted, all gesture related - interrupts are unmasked. - */ - uint8_t GIEN : 2; - - uint8_t get(){ - return (GIEN << 1) | GMODE; - } - void set(uint8_t data){ - GIEN = (data >> 1) & 0x01; - GMODE = data & 0x01; - } - }; - gconf4 _gconf4; - - struct gstatus { - /* Gesture FIFO Data. GVALID bit is sent when GFLVL becomes greater than GFIFOTH (i.e. FIFO has - enough data to set GINT). GFIFOD is reset when GMODE = 0 and the GFLVL=0 (i.e. All FIFO data - has been read). - */ - uint8_t GVALID : 1; - - /* Gesture FIFO Overflow. A setting of 1 indicates that the FIFO has filled to capacity and that new - gesture detector data has been lost. - */ - uint8_t GFOV : 1; - - void set(uint8_t data){ - GFOV = (data >> 1) & 0x01; - GVALID = data & 0x01; - } - }; - gstatus _gstatus; - + + // power on + uint8_t PON : 1; + + // ALS enable + uint8_t AEN : 1; + + // Proximity detect enable + uint8_t PEN : 1; + + // wait timer enable + uint8_t WEN : 1; + + // ALS interrupt enable + uint8_t AIEN : 1; + + // proximity interrupt enable + uint8_t PIEN : 1; + + // gesture enable + uint8_t GEN : 1; + + uint8_t get() { + return (GEN << 6) | (PIEN << 5) | (AIEN << 4) | (WEN << 3) | (PEN << 2) | + (AEN << 1) | PON; + }; + }; + struct enable _enable; + + struct pers { + // ALS Interrupt Persistence. Controls rate of Clear channel interrupt to + // the host processor + uint8_t APERS : 4; + + // proximity interrupt persistence, controls rate of prox interrupt to host + // processor + uint8_t PPERS : 4; + + uint8_t get() { return (PPERS << 4) | APERS; }; + }; + pers _pers; + + struct config1 { + uint8_t WLONG : 1; + + uint8_t get() { return WLONG << 1; }; + }; + config1 _config1; + + struct ppulse { + + /*Proximity Pulse Count. Specifies the number of proximity pulses to be + generated on LDR. Number of pulses is set by PPULSE value plus 1. + */ + uint8_t PPULSE : 6; + + // Proximity Pulse Length. Sets the LED-ON pulse width during a proximity + // LDR pulse. + uint8_t PPLEN : 2; + + uint8_t get() { return (PPLEN << 6) | PPULSE; } + }; + ppulse _ppulse; + + struct control { + // ALS and Color gain control + uint8_t AGAIN : 2; + + // proximity gain control + uint8_t PGAIN : 2; + + // led drive strength + uint8_t LDRIVE : 2; + + uint8_t get() { return (LDRIVE << 6) | (PGAIN << 2) | AGAIN; } + }; + control _control; + + struct config2 { + /* Additional LDR current during proximity and gesture LED pulses. Current + value, set by LDRIVE, is increased by the percentage of LED_BOOST. + */ + uint8_t LED_BOOST : 2; + + // clear photodiode saturation int enable + uint8_t CPSIEN : 1; + + // proximity saturation interrupt enable + uint8_t PSIEN : 1; + + uint8_t get() { + return (PSIEN << 7) | (CPSIEN << 6) | (LED_BOOST << 4) | 1; + } + }; + config2 _config2; + + struct status { + /* ALS Valid. Indicates that an ALS cycle has completed since AEN was + asserted or since a read from any of the ALS/Color data registers. + */ + uint8_t AVALID : 1; + + /* Proximity Valid. Indicates that a proximity cycle has completed since PEN + was asserted or since PDATA was last read. A read of PDATA automatically + clears PVALID. + */ + uint8_t PVALID : 1; + + /* Gesture Interrupt. GINT is asserted when GFVLV becomes greater than + GFIFOTH or if GVALID has become asserted when GMODE transitioned to zero. + The bit is reset when FIFO is completely emptied (read). + */ + uint8_t GINT : 1; + + // ALS Interrupt. This bit triggers an interrupt if AIEN in ENABLE is set. + uint8_t AINT : 1; + + // Proximity Interrupt. This bit triggers an interrupt if PIEN in ENABLE is + // set. + uint8_t PINT : 1; + + /* Indicates that an analog saturation event occurred during a previous + proximity or gesture cycle. Once set, this bit remains set until cleared by + clear proximity interrupt special function command (0xE5 PICLEAR) or by + disabling Prox (PEN=0). This bit triggers an interrupt if PSIEN is set. + */ + uint8_t PGSAT : 1; + + /* Clear Photodiode Saturation. When asserted, the analog sensor was at the + upper end of its dynamic range. The bit can be de-asserted by sending a + Clear channel interrupt command (0xE6 CICLEAR) or by disabling the ADC + (AEN=0). This bit triggers an interrupt if CPSIEN is set. + */ + uint8_t CPSAT : 1; + + void set(uint8_t data) { + AVALID = data & 0x01; + PVALID = (data >> 1) & 0x01; + GINT = (data >> 2) & 0x01; + AINT = (data >> 4) & 0x01; + PINT = (data >> 5) & 0x01; + PGSAT = (data >> 6) & 0x01; + CPSAT = (data >> 7) & 0x01; + } + }; + status _status; + + struct config3 { + // proximity mask + uint8_t PMASK_R : 1; + uint8_t PMASK_L : 1; + uint8_t PMASK_D : 1; + uint8_t PMASK_U : 1; + + /* Sleep After Interrupt. When enabled, the device will automatically enter + low power mode when the INT pin is asserted and the state machine has + progressed to the SAI decision block. Normal operation is resumed when INT + pin is cleared over I2C. + */ + uint8_t SAI : 1; + + /* Proximity Gain Compensation Enable. This bit provides gain compensation + when proximity photodiode signal is reduced as a result of sensor masking. + If only one diode of the diode pair is contributing, then only half of the + signal is available at the ADC; this results in a maximum ADC value of 127. + Enabling PCMP enables an additional gain of 2X, resulting in a maximum ADC + value of 255. + */ + uint8_t PCMP : 1; + + uint8_t get() { + return (PCMP << 5) | (SAI << 4) | (PMASK_U << 3) | (PMASK_D << 2) | + (PMASK_L << 1) | PMASK_R; + } + }; + config3 _config3; + + struct gconf1 { + /* Gesture Exit Persistence. When a number of consecutive “gesture end” + occurrences become equal or greater to the GEPERS value, the Gesture state + machine is exited. + */ + uint8_t GEXPERS : 2; + + /* Gesture Exit Mask. Controls which of the gesture detector photodiodes + (UDLR) will be included to determine a “gesture end” and subsequent exit + of the gesture state machine. Unmasked UDLR data will be compared with the + value in GTHR_OUT. Field value bits correspond to UDLR detectors. + */ + uint8_t GEXMSK : 4; + + /* Gesture FIFO Threshold. This value is compared with the FIFO Level (i.e. + the number of UDLR datasets) to generate an interrupt (if enabled). + */ + uint8_t GFIFOTH : 2; + + uint8_t get() { return (GFIFOTH << 6) | (GEXMSK << 2) | GEXPERS; } + }; + gconf1 _gconf1; + + struct gconf2 { + /* Gesture Wait Time. The GWTIME controls the amount of time in a low power + mode between gesture detection cycles. + */ + uint8_t GWTIME : 3; + + // Gesture LED Drive Strength. Sets LED Drive Strength in gesture mode. + uint8_t GLDRIVE : 2; + + // Gesture Gain Control. Sets the gain of the proximity receiver in gesture + // mode. + uint8_t GGAIN : 2; + + uint8_t get() { return (GGAIN << 5) | (GLDRIVE << 3) | GWTIME; } + }; + gconf2 _gconf2; + + struct gpulse { + /* Number of Gesture Pulses. Specifies the number of pulses to be generated + on LDR. Number of pulses is set by GPULSE value plus 1. + */ + uint8_t GPULSE : 6; + + // Gesture Pulse Length. Sets the LED_ON pulse width during a Gesture LDR + // Pulse. + uint8_t GPLEN : 2; + + uint8_t get() { return (GPLEN << 6) | GPULSE; } + }; + gpulse _gpulse; + + struct gconf3 { + /* Gesture Dimension Select. Selects which gesture photodiode pairs are + enabled to gather results during gesture. + */ + uint8_t GDIMS : 2; + + uint8_t get() { return GDIMS; } + }; + gconf3 _gconf3; + + struct gconf4 { + /* Gesture Mode. Reading this bit reports if the gesture state machine is + actively running, 1 = Gesture, 0= ALS, Proximity, Color. Writing a 1 to this + bit causes immediate entry in to the gesture state machine (as if GPENTH had + been exceeded). Writing a 0 to this bit causes exit of gesture when current + analog conversion has finished (as if GEXTH had been exceeded). + */ + uint8_t GMODE : 1; + + /* Gesture interrupt enable. Gesture Interrupt Enable. When asserted, all + gesture related interrupts are unmasked. + */ + uint8_t GIEN : 2; + + uint8_t get() { return (GIEN << 1) | GMODE; } + void set(uint8_t data) { + GIEN = (data >> 1) & 0x01; + GMODE = data & 0x01; + } + }; + gconf4 _gconf4; + + struct gstatus { + /* Gesture FIFO Data. GVALID bit is sent when GFLVL becomes greater than + GFIFOTH (i.e. FIFO has enough data to set GINT). GFIFOD is reset when GMODE + = 0 and the GFLVL=0 (i.e. All FIFO data has been read). + */ + uint8_t GVALID : 1; + + /* Gesture FIFO Overflow. A setting of 1 indicates that the FIFO has filled + to capacity and that new gesture detector data has been lost. + */ + uint8_t GFOV : 1; + + void set(uint8_t data) { + GFOV = (data >> 1) & 0x01; + GVALID = data & 0x01; + } + }; + gstatus _gstatus; }; #endif diff --git a/firmware/libraries/Adafruit_APDS9960_Library/README.md b/firmware/libraries/Adafruit_APDS9960_Library/README.md new file mode 100644 index 0000000..b115d7c --- /dev/null +++ b/firmware/libraries/Adafruit_APDS9960_Library/README.md @@ -0,0 +1,18 @@ +# Adafruit APDS9960 Library [![Build Status](https://travis-ci.com/adafruit/Adafruit_APDS9960.svg?branch=master)](https://travis-ci.com/adafruit/Adafruit_APDS9960) + + + +This is the Adafruit APDS9960 Proximity, Light, RGB, and Gesture sensor Library + +Tested and works great with the Adafruit APDS9960 Board +* http://www.adafruit.com/products/3595 + +This chip uses I2C to communicate, 2 pins are required to interface + +Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit! + +Written by Dean Miller, Limor Fried for Adafruit Industries. +BSD license, check license.txt for more information +All text above must be included in any redistribution + +To install, use the Arduino Library Manager and search for "Adafruit APDS9960 Library" and install the library. diff --git a/firmware/libraries/Adafruit_APDS9960_Library/assets/board.jpg b/firmware/libraries/Adafruit_APDS9960_Library/assets/board.jpg new file mode 100644 index 0000000000000000000000000000000000000000..dafb0d377739e4744f15c6885c7185992d5e29a9 GIT binary patch literal 291777 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ξD~LM;&*V$adC~|R#+tc*+ZQ6 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