浏览代码

首次推送

kinve 4 年之前
当前提交
00ecef3b11
共有 11 个文件被更改,包括 5635 次插入0 次删除
  1. 二进制
      APDS9930-master.zip
  2. 1147 0
      licb/APDS9930.cpp
  3. 231 0
      licb/APDS9930.h
  4. 2208 0
      licb/SparkFun_APDS9960.cpp
  5. 347 0
      licb/SparkFun_APDS9960.h
  6. 479 0
      neo-sphere/neo-sphere.ino
  7. 595 0
      neo-sphere/neo-sphere.ino.standard.hex
  8. 628 0
      neo-sphere/neo-sphere.ino.with_bootloader.standard.hex
  9. 二进制
      ring.stl
  10. 二进制
      templateA.stl
  11. 二进制
      templateB.stl

二进制
APDS9930-master.zip


+ 1147 - 0
licb/APDS9930.cpp

@@ -0,0 +1,1147 @@
+/**
+ * @file    APDS-9930.cpp
+ * @brief   Library for the SparkFun APDS-9930 breakout board
+ * @author  Shawn Hymel (SparkFun Electronics)
+ *
+ * @copyright	This code is public domain but you buy me a beer if you use
+ * this and we meet someday (Beerware license).
+ *
+ * This library interfaces the Avago APDS-9930 to Arduino over I2C. The library
+ * relies on the Arduino Wire (I2C) library. to use the library, instantiate an
+ * APDS9930 object, call init(), and call the appropriate functions.
+ *
+ * APDS-9930 current draw tests (default parameters):
+ *   Off:                   1mA
+ *   Waiting for gesture:   14mA
+ *   Gesture in progress:   35mA
+ */
+ 
+ #include <Arduino.h>
+ #include <Wire.h>
+ 
+ #include "APDS9930.h"
+ 
+/**
+ * @brief Constructor - Instantiates APDS9930 object
+ */
+APDS9930::APDS9930()
+{
+
+}
+ 
+/**
+ * @brief Destructor
+ */
+APDS9930::~APDS9930()
+{
+
+}
+
+/**
+ * @brief Configures I2C communications and initializes registers to defaults
+ *
+ * @return True if initialized successfully. False otherwise.
+ */
+bool APDS9930::init()
+{
+    uint8_t id;
+
+    /* Initialize I2C */
+    Wire.begin();
+     
+    /* Read ID register and check against known values for APDS-9930 */
+    if( !wireReadDataByte(APDS9930_ID, id) ) {
+        Serial.println(F("ID read"));
+        return false;
+    }
+    if( !(id == APDS9930_ID_1 || id == APDS9930_ID_2) ) {
+        Serial.println(F("ID check"));
+        Serial.println(String("ID is ") + String(id, HEX));
+        //return false;
+    }
+     
+    /* Set ENABLE register to 0 (disable all features) */
+    if( !setMode(ALL, OFF) ) {
+        Serial.println(F("Regs off"));
+        return false;
+    }
+    
+    /* Set default values for ambient light and proximity registers */
+    if( !wireWriteDataByte(APDS9930_ATIME, DEFAULT_ATIME) ) {
+        return false;
+    }
+    if( !wireWriteDataByte(APDS9930_WTIME, DEFAULT_WTIME) ) {
+        return false;
+    }
+    if( !wireWriteDataByte(APDS9930_PPULSE, DEFAULT_PPULSE) ) {
+        return false;
+    }
+    if( !wireWriteDataByte(APDS9930_POFFSET, DEFAULT_POFFSET) ) {
+        return false;
+    }
+    if( !wireWriteDataByte(APDS9930_CONFIG, DEFAULT_CONFIG) ) {
+        return false;
+    }
+    if( !setLEDDrive(DEFAULT_PDRIVE) ) {
+        return false;
+    }
+    if( !setProximityGain(DEFAULT_PGAIN) ) {
+        return false;
+    }
+    if( !setAmbientLightGain(DEFAULT_AGAIN) ) {
+        return false;
+    }
+    if( !setProximityDiode(DEFAULT_PDIODE) ) {
+        return false;
+    }
+    if( !setProximityIntLowThreshold(DEFAULT_PILT) ) {
+        return false;
+    }
+    if( !setProximityIntHighThreshold(DEFAULT_PIHT) ) {
+        return false;
+    }
+    if( !setLightIntLowThreshold(DEFAULT_AILT) ) {
+        return false;
+    }
+    if( !setLightIntHighThreshold(DEFAULT_AIHT) ) {
+        return false;
+    }
+    if( !wireWriteDataByte(APDS9930_PERS, DEFAULT_PERS) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/*******************************************************************************
+ * Public methods for controlling the APDS-9930
+ ******************************************************************************/
+
+/**
+ * @brief Reads and returns the contents of the ENABLE register
+ *
+ * @return Contents of the ENABLE register. 0xFF if error.
+ */
+uint8_t APDS9930::getMode()
+{
+    uint8_t enable_value;
+    
+    /* Read current ENABLE register */
+    if( !wireReadDataByte(APDS9930_ENABLE, enable_value) ) {
+        return ERROR;
+    }
+    
+    return enable_value;
+}
+
+/**
+ * @brief Enables or disables a feature in the APDS-9930
+ *
+ * @param[in] mode which feature to enable
+ * @param[in] enable ON (1) or OFF (0)
+ * @return True if operation success. False otherwise.
+ */
+bool APDS9930::setMode(uint8_t mode, uint8_t enable)
+{
+    uint8_t reg_val;
+
+    /* Read current ENABLE register */
+    reg_val = getMode();
+    if( reg_val == ERROR ) {
+        return false;
+    }
+    
+    /* 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 */
+    if( !wireWriteDataByte(APDS9930_ENABLE, reg_val) ) {
+        return false;
+    }
+        
+    return true;
+}
+
+/**
+ * @brief Starts the light (Ambient/IR) sensor on the APDS-9930
+ *
+ * @param[in] interrupts true to enable hardware interrupt on high or low light
+ * @return True if sensor enabled correctly. False on error.
+ */
+bool APDS9930::enableLightSensor(bool interrupts)
+{
+    
+    /* Set default gain, interrupts, enable power, and enable sensor */
+    if( !setAmbientLightGain(DEFAULT_AGAIN) ) {
+        return false;
+    }
+    if( interrupts ) {
+        if( !setAmbientLightIntEnable(1) ) {
+            return false;
+        }
+    } else {
+        if( !setAmbientLightIntEnable(0) ) {
+            return false;
+        }
+    }
+    if( !enablePower() ){
+        return false;
+    }
+    if( !setMode(AMBIENT_LIGHT, 1) ) {
+        return false;
+    }
+    
+    return true;
+
+}
+
+/**
+ * @brief Ends the light sensor on the APDS-9930
+ *
+ * @return True if sensor disabled correctly. False on error.
+ */
+bool APDS9930::disableLightSensor()
+{
+    if( !setAmbientLightIntEnable(0) ) {
+        return false;
+    }
+    if( !setMode(AMBIENT_LIGHT, 0) ) {
+        return false;
+    }
+    
+    return true;
+}
+
+/**
+ * @brief Starts the proximity sensor on the APDS-9930
+ *
+ * @param[in] interrupts true to enable hardware external interrupt on proximity
+ * @return True if sensor enabled correctly. False on error.
+ */
+bool APDS9930::enableProximitySensor(bool interrupts)
+{
+    /* Set default gain, LED, interrupts, enable power, and enable sensor */
+    if( !setProximityGain(DEFAULT_PGAIN) ) {
+        return false;
+    }
+    if( !setLEDDrive(DEFAULT_PDRIVE) ) {
+        return false;
+    }
+    if( interrupts ) {
+        if( !setProximityIntEnable(1) ) {
+            return false;
+        }
+    } else {
+        if( !setProximityIntEnable(0) ) {
+            return false;
+        }
+    }
+    if( !enablePower() ){
+        return false;
+    }
+    if( !setMode(PROXIMITY, 1) ) {
+        return false;
+    }
+    
+    return true;
+}
+
+/**
+ * @brief Ends the proximity sensor on the APDS-9930
+ *
+ * @return True if sensor disabled correctly. False on error.
+ */
+bool APDS9930::disableProximitySensor()
+{
+	if( !setProximityIntEnable(0) ) {
+		return false;
+	}
+	if( !setMode(PROXIMITY, 0) ) {
+		return false;
+	}
+
+	return true;
+}
+
+/**
+ * Turn the APDS-9930 on
+ *
+ * @return True if operation successful. False otherwise.
+ */
+bool APDS9930::enablePower()
+{
+    if( !setMode(POWER, 1) ) {
+        return false;
+    }
+    
+    return true;
+}
+
+/**
+ * Turn the APDS-9930 off
+ *
+ * @return True if operation successful. False otherwise.
+ */
+bool APDS9930::disablePower()
+{
+    if( !setMode(POWER, 0) ) {
+        return false;
+    }
+    
+    return true;
+}
+
+/*******************************************************************************
+ * Ambient light sensor controls
+ ******************************************************************************/
+
+/**
+ * @brief Reads the ambient (clear) light level as a 16-bit value
+ *
+ * @param[out] val value of the light sensor.
+ * @return True if operation successful. False otherwise.
+ */
+bool APDS9930::readAmbientLightLux(float &val)
+{
+    uint16_t Ch0;
+    uint16_t Ch1;
+    
+    /* Read value from channel 0 */
+    if( !readCh0Light(Ch0) ) {
+        return false;
+    }
+
+    /* Read value from channel 1 */
+    if( !readCh1Light(Ch1) ) {
+        return false;
+    }
+
+    val = floatAmbientToLux(Ch0, Ch1);
+    return true;
+}
+
+bool APDS9930::readAmbientLightLux(unsigned long &val)
+{
+    uint16_t Ch0;
+    uint16_t Ch1;
+    
+    /* Read value from channel 0 */
+    if( !readCh0Light(Ch0) ) {
+        return false;
+    }
+
+    /* Read value from channel 1 */
+    if( !readCh1Light(Ch1) ) {
+        return false;
+    }
+
+    val = ulongAmbientToLux(Ch0, Ch1);
+    return true;
+}
+
+float APDS9930::floatAmbientToLux(uint16_t Ch0, uint16_t Ch1)
+{
+	uint8_t x[4]={1,8,16,120};
+    float ALSIT = 2.73 * (256 - DEFAULT_ATIME);
+    float iac  = max(Ch0 - ALS_B * Ch1, ALS_C * Ch0 - ALS_D * Ch1);
+    if (iac < 0) iac = 0;
+	float lpc  = GA * DF / (ALSIT * x[getAmbientLightGain()]);
+    return iac * lpc;
+}
+
+unsigned long APDS9930::ulongAmbientToLux(uint16_t Ch0, uint16_t Ch1)
+{
+	uint8_t x[4]={1,8,16,120};
+    unsigned long ALSIT = 2.73 * (256 - DEFAULT_ATIME);
+    unsigned long iac  = max(Ch0 - ALS_B * Ch1, ALS_C * Ch0 - ALS_D * Ch1);
+	if (iac < 0) iac = 0;
+    unsigned long lpc  = GA * DF / (ALSIT * x[getAmbientLightGain()]);
+    return iac * lpc;
+}
+
+bool APDS9930::readCh0Light(uint16_t &val)
+{
+    uint8_t val_byte;
+    val = 0;
+    
+    /* Read value from channel 0 */
+    if( !wireReadDataByte(APDS9930_Ch0DATAL, val_byte) ) {
+        return false;
+    }
+    val = val_byte;
+    if( !wireReadDataByte(APDS9930_Ch0DATAH, val_byte) ) {
+        return false;
+    }
+    val += ((uint16_t)val_byte << 8);
+    return true;
+}
+
+bool APDS9930::readCh1Light(uint16_t &val)
+{
+    uint8_t val_byte;
+    val = 0;
+    
+    /* Read value from channel 0 */
+    if( !wireReadDataByte(APDS9930_Ch1DATAL, val_byte) ) {
+        return false;
+    }
+    val = val_byte;
+    if( !wireReadDataByte(APDS9930_Ch1DATAH, val_byte) ) {
+        return false;
+    }
+    val += ((uint16_t)val_byte << 8);
+    return true;
+}
+
+/*******************************************************************************
+ * Proximity sensor controls
+ ******************************************************************************/
+
+/**
+ * @brief Reads the proximity level as an 8-bit value
+ *
+ * @param[out] val value of the proximity sensor.
+ * @return True if operation successful. False otherwise.
+ */
+bool APDS9930::readProximity(uint16_t &val)
+{
+    val = 0;
+    uint8_t val_byte;
+
+    /* Read value from proximity data register */
+    if( !wireReadDataByte(APDS9930_PDATAL, val_byte) ) {
+        return false;
+    }
+    val = val_byte;
+    if( !wireReadDataByte(APDS9930_PDATAH, val_byte) ) {
+        return false;
+    }
+    val += ((uint16_t)val_byte << 8);
+    
+    return true;
+}
+
+/*******************************************************************************
+ * Getters and setters for register values
+ ******************************************************************************/
+
+/**
+ * @brief Returns the lower threshold for proximity detection
+ *
+ * @return lower threshold
+ */
+uint16_t APDS9930::getProximityIntLowThreshold()
+{
+    uint16_t val;
+    uint8_t val_byte;
+    
+    /* Read value from PILT register */
+    if( !wireReadDataByte(APDS9930_PILTL, val_byte) ) {
+        val = 0;
+    }
+    val = val_byte;
+    if( !wireReadDataByte(APDS9930_PILTH, val_byte) ) {
+        val = 0;
+    }
+    val |= ((uint16_t)val_byte << 8);    
+    
+    return val;
+}
+
+/**
+ * @brief Sets the lower threshold for proximity detection
+ *
+ * @param[in] threshold the lower proximity threshold
+ * @return True if operation successful. False otherwise.
+ */
+bool APDS9930::setProximityIntLowThreshold(uint16_t threshold)
+{
+    uint8_t lo;
+    uint8_t hi;
+    hi = threshold >> 8;
+    lo = threshold & 0x00FF;
+
+    if( !wireWriteDataByte(APDS9930_PILTL, lo) ) {
+        return false;
+    }
+    if( !wireWriteDataByte(APDS9930_PILTH, hi) ) {
+        return false;
+    }
+    
+    return true;
+}
+
+/**
+ * @brief Returns the high threshold for proximity detection
+ *
+ * @return high threshold
+ */
+uint16_t APDS9930::getProximityIntHighThreshold()
+{
+    uint16_t val;
+    uint8_t val_byte;
+    
+    /* Read value from PILT register */
+    if( !wireReadDataByte(APDS9930_PIHTL, val_byte) ) {
+        val = 0;
+    }
+    val = val_byte;
+    if( !wireReadDataByte(APDS9930_PIHTH, val_byte) ) {
+        val = 0;
+    }
+    val |= ((uint16_t)val_byte << 8);    
+    
+    return val;
+}
+
+/**
+ * @brief Sets the high threshold for proximity detection
+ *
+ * @param[in] threshold the high proximity threshold
+ * @return True if operation successful. False otherwise.
+ */
+bool APDS9930::setProximityIntHighThreshold(uint16_t threshold)
+{
+    uint8_t lo;
+    uint8_t hi;
+    hi = threshold >> 8;
+    lo = threshold & 0x00FF;
+
+    if( !wireWriteDataByte(APDS9930_PIHTL, lo) ) {
+        return false;
+    }
+    if( !wireWriteDataByte(APDS9930_PIHTH, hi) ) {
+        return false;
+    }
+    
+    return true;
+}
+
+/**
+ * @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 APDS9930::getLEDDrive()
+{
+    uint8_t val;
+    
+    /* Read value from CONTROL register */
+    if( !wireReadDataByte(APDS9930_CONTROL, val) ) {
+        return ERROR;
+    }
+    
+    /* 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
+ * @return True if operation successful. False otherwise.
+ */
+bool APDS9930::setLEDDrive(uint8_t drive)
+{
+    uint8_t val;
+    
+    /* Read value from CONTROL register */
+    if( !wireReadDataByte(APDS9930_CONTROL, val) ) {
+        return false;
+    }
+    
+    /* Set bits in register to given value */
+    drive &= 0b00000011;
+    drive = drive << 6;
+    val &= 0b00111111;
+    val |= drive;
+    
+    /* Write register value back into CONTROL register */
+    if( !wireWriteDataByte(APDS9930_CONTROL, val) ) {
+        return false;
+    }
+    
+    return true;
+}
+
+/**
+ * @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 APDS9930::getProximityGain()
+{
+    uint8_t val;
+    
+    /* Read value from CONTROL register */
+    if( !wireReadDataByte(APDS9930_CONTROL, val) ) {
+        return ERROR;
+    }
+    
+    /* 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
+ * @return True if operation successful. False otherwise.
+ */
+bool APDS9930::setProximityGain(uint8_t drive)
+{
+    uint8_t val;
+    
+    /* Read value from CONTROL register */
+    if( !wireReadDataByte(APDS9930_CONTROL, val) ) {
+        return false;
+    }
+    
+    /* Set bits in register to given value */
+    drive &= 0b00000011;
+    drive = drive << 2;
+    val &= 0b11110011;
+    val |= drive;
+    
+    /* Write register value back into CONTROL register */
+    if( !wireWriteDataByte(APDS9930_CONTROL, val) ) {
+        return false;
+    }
+    
+    return true;
+}
+
+/**
+ * @brief Returns the proximity diode
+ *
+ * Value    Diode selection
+ *   0       Reserved
+ *   1       Reserved
+ *   2       Use Ch1 diode
+ *   3       Reserved
+ *
+ * @return the selected diode. 0xFF on failure.
+ */
+uint8_t APDS9930::getProximityDiode()
+{
+    uint8_t val;
+    
+    /* Read value from CONTROL register */
+    if( !wireReadDataByte(APDS9930_CONTROL, val) ) {
+        return ERROR;
+    }
+    
+    /* Shift and mask out PDRIVE bits */
+    val = (val >> 4) & 0b00000011;
+    
+    return val;
+}
+
+/**
+ * @brief Selects the proximity diode
+ *
+ * Value    Diode selection
+ *   0       Reserved
+ *   1       Reserved
+ *   2       Use Ch1 diode
+ *   3       Reserved
+ *
+ * @param[in] drive the value (0-3) for the diode
+ * @return True if operation successful. False otherwise.
+ */
+bool APDS9930::setProximityDiode(uint8_t drive)
+{
+    uint8_t val;
+    
+    /* Read value from CONTROL register */
+    if( !wireReadDataByte(APDS9930_CONTROL, val) ) {
+        return false;
+    }
+    
+    /* Set bits in register to given value */
+    drive &= 0b00000011;
+    drive = drive << 4;
+    val &= 0b11001111;
+    val |= drive;
+    
+    /* Write register value back into CONTROL register */
+    if( !wireWriteDataByte(APDS9930_CONTROL, val) ) {
+        return false;
+    }
+    
+    return true;
+}
+
+/**
+ * @brief Returns receiver gain for the ambient light sensor (ALS)
+ *
+ * Value    Gain
+ *   0        1x
+ *   1        4x
+ *   2       16x
+ *   3      120x
+ *
+ * @return the value of the ALS gain. 0xFF on failure.
+ */
+uint8_t APDS9930::getAmbientLightGain()
+{
+    uint8_t val;
+    
+    /* Read value from CONTROL register */
+    if( !wireReadDataByte(APDS9930_CONTROL, val) ) {
+        return ERROR;
+    }
+    
+    /* Shift and mask out ADRIVE bits */
+    val &= 0b00000011;
+	
+    return val;
+}
+
+/**
+ * @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
+ * @return True if operation successful. False otherwise.
+ */
+bool APDS9930::setAmbientLightGain(uint8_t drive)
+{
+    uint8_t val;
+    
+    /* Read value from CONTROL register */
+    if( !wireReadDataByte(APDS9930_CONTROL, val) ) {
+        return false;
+    }
+    
+    /* Set bits in register to given value */
+    drive &= 0b00000011;
+    val &= 0b11111100;
+    val |= drive;
+    
+    /* Write register value back into CONTROL register */
+    if( !wireWriteDataByte(APDS9930_CONTROL, val) ) {
+        return false;
+    }
+    
+    return true;
+}
+
+/**
+ * @brief Gets the low threshold for ambient light interrupts
+ *
+ * @param[out] threshold current low threshold stored on the APDS-9930
+ * @return True if operation successful. False otherwise.
+ */
+bool APDS9930::getLightIntLowThreshold(uint16_t &threshold)
+{
+    uint8_t val_byte;
+    threshold = 0;
+    
+    /* Read value from ambient light low threshold, low byte register */
+    if( !wireReadDataByte(APDS9930_AILTL, val_byte) ) {
+        return false;
+    }
+    threshold = val_byte;
+    
+    /* Read value from ambient light low threshold, high byte register */
+    if( !wireReadDataByte(APDS9930_AILTH, val_byte) ) {
+        return false;
+    }
+    threshold = threshold + ((uint16_t)val_byte << 8);
+    
+    return true;
+}
+
+/**
+ * @brief Sets the low threshold for ambient light interrupts
+ *
+ * @param[in] threshold low threshold value for interrupt to trigger
+ * @return True if operation successful. False otherwise.
+ */
+bool APDS9930::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 */
+    if( !wireWriteDataByte(APDS9930_AILTL, val_low) ) {
+        return false;
+    }
+    
+    /* Write high byte */
+    if( !wireWriteDataByte(APDS9930_AILTH, val_high) ) {
+        return false;
+    }
+    
+    return true;
+}
+
+/**
+ * @brief Gets the high threshold for ambient light interrupts
+ *
+ * @param[out] threshold current low threshold stored on the APDS-9930
+ * @return True if operation successful. False otherwise.
+ */
+bool APDS9930::getLightIntHighThreshold(uint16_t &threshold)
+{
+    uint8_t val_byte;
+    threshold = 0;
+    
+    /* Read value from ambient light high threshold, low byte register */
+    if( !wireReadDataByte(APDS9930_AIHTL, val_byte) ) {
+        return false;
+    }
+    threshold = val_byte;
+    
+    /* Read value from ambient light high threshold, high byte register */
+    if( !wireReadDataByte(APDS9930_AIHTH, val_byte) ) {
+        return false;
+    }
+    threshold = threshold + ((uint16_t)val_byte << 8);
+    
+    return true;
+}
+
+/**
+ * @brief Sets the high threshold for ambient light interrupts
+ *
+ * @param[in] threshold high threshold value for interrupt to trigger
+ * @return True if operation successful. False otherwise.
+ */
+bool APDS9930::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 */
+    if( !wireWriteDataByte(APDS9930_AIHTL, val_low) ) {
+        return false;
+    }
+    
+    /* Write high byte */
+    if( !wireWriteDataByte(APDS9930_AIHTH, val_high) ) {
+        return false;
+    }
+    
+    return true;
+}
+
+
+/**
+ * @brief Gets if ambient light interrupts are enabled or not
+ *
+ * @return 1 if interrupts are enabled, 0 if not. 0xFF on error.
+ */
+uint8_t APDS9930::getAmbientLightIntEnable()
+{
+    uint8_t val;
+    
+    /* Read value from ENABLE register */
+    if( !wireReadDataByte(APDS9930_ENABLE, val) ) {
+        return ERROR;
+    }
+    
+    /* 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
+ * @return True if operation successful. False otherwise.
+ */
+bool APDS9930::setAmbientLightIntEnable(uint8_t enable)
+{
+    uint8_t val;
+    
+    /* Read value from ENABLE register */
+    if( !wireReadDataByte(APDS9930_ENABLE, val) ) {
+        return false;
+    }
+    
+    /* Set bits in register to given value */
+    enable &= 0b00000001;
+    enable = enable << 4;
+    val &= 0b11101111;
+    val |= enable;
+    
+    /* Write register value back into ENABLE register */
+    if( !wireWriteDataByte(APDS9930_ENABLE, val) ) {
+        return false;
+    }
+    
+    return true;
+}
+
+/**
+ * @brief Gets if proximity interrupts are enabled or not
+ *
+ * @return 1 if interrupts are enabled, 0 if not. 0xFF on error.
+ */
+uint8_t APDS9930::getProximityIntEnable()
+{
+    uint8_t val;
+    
+    /* Read value from ENABLE register */
+    if( !wireReadDataByte(APDS9930_ENABLE, val) ) {
+        return ERROR;
+    }
+    
+    /* 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
+ * @return True if operation successful. False otherwise.
+ */
+bool APDS9930::setProximityIntEnable(uint8_t enable)
+{
+    uint8_t val;
+    
+    /* Read value from ENABLE register */
+    if( !wireReadDataByte(APDS9930_ENABLE, val) ) {
+        return false;
+    }
+    
+    /* Set bits in register to given value */
+    enable &= 0b00000001;
+    enable = enable << 5;
+    val &= 0b11011111;
+    val |= enable;
+    
+    /* Write register value back into ENABLE register */
+    if( !wireWriteDataByte(APDS9930_ENABLE, val) ) {
+        return false;
+    }
+    
+    return true;
+}
+
+/**
+ * @brief Clears the ambient light interrupt
+ *
+ * @return True if operation completed successfully. False otherwise.
+ */
+bool APDS9930::clearAmbientLightInt()
+{
+    if( !wireWriteByte(CLEAR_ALS_INT) ) {
+        return false;
+    }
+    
+    return true;
+}
+
+/**
+ * @brief Clears the proximity interrupt
+ *
+ * @return True if operation completed successfully. False otherwise.
+ */
+bool APDS9930::clearProximityInt()
+{
+    if( !wireWriteByte(CLEAR_PROX_INT) ) {
+        return false;
+    }
+    
+    return true;
+}
+
+/**
+ * @brief Clears all interrupts
+ *
+ * @return True if operation completed successfully. False otherwise.
+ */
+bool APDS9930::clearAllInts()
+{
+    if( !wireWriteByte(CLEAR_ALL_INTS) ) {
+        return false;
+    }
+    
+    return true;
+}
+
+/*******************************************************************************
+ * Raw I2C Reads and Writes
+ ******************************************************************************/
+
+/**
+ * @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 APDS9930::wireWriteByte(uint8_t val)
+{
+    Wire.beginTransmission(APDS9930_I2C_ADDR);
+    Wire.write(val);
+    if( Wire.endTransmission() != 0 ) {
+        return false;
+    }
+    
+    return true;
+}
+
+/**
+ * @brief Writes a single byte to the I2C device and specified register
+ *
+ * @param[in] reg the register in the I2C device to write to
+ * @param[in] val the 1-byte value to write to the I2C device
+ * @return True if successful write operation. False otherwise.
+ */
+bool APDS9930::wireWriteDataByte(uint8_t reg, uint8_t val)
+{
+    Wire.beginTransmission(APDS9930_I2C_ADDR);
+    Wire.write(reg | AUTO_INCREMENT);
+    Wire.write(val);
+    if( Wire.endTransmission() != 0 ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @brief Writes a block (array) of bytes to the I2C device and register
+ *
+ * @param[in] reg the register in the I2C device to write to
+ * @param[in] val pointer to the beginning of the data byte array
+ * @param[in] len the length (in bytes) of the data to write
+ * @return True if successful write operation. False otherwise.
+ */
+bool APDS9930::wireWriteDataBlock(  uint8_t reg, 
+                                        uint8_t *val, 
+                                        unsigned int len)
+{
+    unsigned int i;
+
+    Wire.beginTransmission(APDS9930_I2C_ADDR);
+    Wire.write(reg | AUTO_INCREMENT);
+    for(i = 0; i < len; i++) {
+        Wire.beginTransmission(APDS9930_I2C_ADDR);
+        Wire.write(val[i]);
+    }
+    if( Wire.endTransmission() != 0 ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @brief Reads a single byte from the I2C device and specified register
+ *
+ * @param[in] reg the register to read from
+ * @param[out] the value returned from the register
+ * @return True if successful read operation. False otherwise.
+ */
+bool APDS9930::wireReadDataByte(uint8_t reg, uint8_t &val)
+{
+    
+    /* Indicate which register we want to read from */
+    if (!wireWriteByte(reg | AUTO_INCREMENT)) {
+        return false;
+    }
+    
+    /* Read from register */
+    Wire.requestFrom(APDS9930_I2C_ADDR, 1);
+    while (Wire.available()) {
+        val = Wire.read();
+    }
+
+    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.
+ */
+int APDS9930::wireReadDataBlock(   uint8_t reg, 
+                                        uint8_t *val, 
+                                        unsigned int len)
+{
+    unsigned char i = 0;
+    
+    /* Indicate which register we want to read from */
+    if (!wireWriteByte(reg | AUTO_INCREMENT)) {
+        return -1;
+    }
+    
+    /* Read block data */
+    Wire.requestFrom(APDS9930_I2C_ADDR, len);
+    while (Wire.available()) {
+        if (i >= len) {
+            return -1;
+        }
+        val[i] = Wire.read();
+        i++;
+    }
+
+    return i;
+}

+ 231 - 0
licb/APDS9930.h

@@ -0,0 +1,231 @@
+/**
+ * @file    APDS-9930.h
+ * @brief   Library for the SparkFun APDS-9930 breakout board
+ * @author  Shawn Hymel (SparkFun Electronics)
+ *
+ * @copyright	This code is public domain but you buy me a beer if you use
+ * this and we meet someday (Beerware license).
+ *
+ * This library interfaces the Avago APDS-9930 to Arduino over I2C. The library
+ * relies on the Arduino Wire (I2C) library. to use the library, instantiate an
+ * APDS9930 object, call init(), and call the appropriate functions.
+ */
+ 
+#ifndef APDS9930_H
+#define APDS9930_H
+
+#include <Arduino.h>
+
+/* Debug */
+#define DEBUG                   0
+
+/* APDS-9930 I2C address */
+#define APDS9930_I2C_ADDR       0x39
+
+/* Command register modes */
+#define REPEATED_BYTE           0x80
+#define AUTO_INCREMENT          0xA0
+#define SPECIAL_FN              0xE0
+
+/* Error code for returned values */
+#define ERROR                   0xFF
+
+/* Acceptable device IDs */
+#define APDS9930_ID_1           0x12
+#define APDS9930_ID_2           0x39
+
+/* Misc parameters */
+#define FIFO_PAUSE_TIME         30      // Wait period (ms) between FIFO reads
+
+/* APDS-9930 register addresses */
+#define APDS9930_ENABLE         0x00
+#define APDS9930_ATIME          0x01
+#define APDS9930_PTIME          0x02
+#define APDS9930_WTIME          0x03
+#define APDS9930_AILTL          0x04
+#define APDS9930_AILTH          0x05
+#define APDS9930_AIHTL          0x06
+#define APDS9930_AIHTH          0x07
+#define APDS9930_PILTL          0x08
+#define APDS9930_PILTH          0x09
+#define APDS9930_PIHTL          0x0A
+#define APDS9930_PIHTH          0x0B
+#define APDS9930_PERS           0x0C
+#define APDS9930_CONFIG         0x0D
+#define APDS9930_PPULSE         0x0E
+#define APDS9930_CONTROL        0x0F
+#define APDS9930_ID             0x12
+#define APDS9930_STATUS         0x13
+#define APDS9930_Ch0DATAL       0x14
+#define APDS9930_Ch0DATAH       0x15
+#define APDS9930_Ch1DATAL       0x16
+#define APDS9930_Ch1DATAH       0x17
+#define APDS9930_PDATAL         0x18
+#define APDS9930_PDATAH         0x19
+#define APDS9930_POFFSET        0x1E
+
+
+/* Bit fields */
+#define APDS9930_PON            0b00000001
+#define APDS9930_AEN            0b00000010
+#define APDS9930_PEN            0b00000100
+#define APDS9930_WEN            0b00001000
+#define APSD9930_AIEN           0b00010000
+#define APDS9930_PIEN           0b00100000
+#define APDS9930_SAI            0b01000000
+
+/* 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 SLEEP_AFTER_INT         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_8X                1
+#define AGAIN_16X               2
+#define AGAIN_120X              3
+
+/* Interrupt clear values */
+#define CLEAR_PROX_INT          0xE5
+#define CLEAR_ALS_INT           0xE6
+#define CLEAR_ALL_INTS          0xE7
+
+/* Default values */
+#define DEFAULT_ATIME           0xED
+#define DEFAULT_WTIME           0xFF
+#define DEFAULT_PTIME           0xFF
+#define DEFAULT_PPULSE          0x08
+#define DEFAULT_POFFSET         0       // 0 offset
+#define DEFAULT_CONFIG          0
+#define DEFAULT_PDRIVE          LED_DRIVE_100MA
+#define DEFAULT_PDIODE          2
+#define DEFAULT_PGAIN           PGAIN_8X
+#define DEFAULT_AGAIN           AGAIN_1X
+#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            0x22    // 2 consecutive prox or ALS for int.
+
+/* ALS coefficients */
+#define DF                      52
+#define GA                      0.49
+#define ALS_B                       1.862
+#define ALS_C                       0.746
+#define ALS_D                       1.291
+
+/* State definitions */
+enum {
+  NOTAVAILABLE_STATE,
+  NEAR_STATE,
+  FAR_STATE,
+  ALL_STATE
+};
+
+#ifdef _AVR_IO_H_
+    // Do not use this alias as it's deprecated
+    #define NA_STATE NOTAVAILABLE_STATE
+#endif
+
+/* APDS9930 Class */
+class APDS9930 {
+public:
+
+    /* Initialization methods */
+    APDS9930();
+    ~APDS9930();
+    bool init();
+    uint8_t getMode();
+    bool setMode(uint8_t mode, uint8_t enable);
+    
+    /* Turn the APDS-9930 on and off */
+    bool enablePower();
+    bool disablePower();
+    
+    /* Enable or disable specific sensors */
+    bool enableLightSensor(bool interrupts = false);
+    bool disableLightSensor();
+    bool enableProximitySensor(bool interrupts = false);
+    bool disableProximitySensor();
+
+    /* LED drive strength control */
+    uint8_t getLEDDrive();
+    bool setLEDDrive(uint8_t drive);
+    // uint8_t getGestureLEDDrive();
+    // bool setGestureLEDDrive(uint8_t drive);
+    
+    /* Gain control */
+    uint8_t getAmbientLightGain();
+    bool setAmbientLightGain(uint8_t gain);
+    uint8_t getProximityGain();
+    bool setProximityGain(uint8_t gain);
+    bool setProximityDiode(uint8_t drive);
+    uint8_t getProximityDiode();
+
+    
+    /* Get and set light interrupt thresholds */
+    bool getLightIntLowThreshold(uint16_t &threshold);
+    bool setLightIntLowThreshold(uint16_t threshold);
+    bool getLightIntHighThreshold(uint16_t &threshold);
+    bool setLightIntHighThreshold(uint16_t threshold);
+    
+    /* Get and set interrupt enables */
+    uint8_t getAmbientLightIntEnable();
+    bool setAmbientLightIntEnable(uint8_t enable);
+    uint8_t getProximityIntEnable();
+    bool setProximityIntEnable(uint8_t enable);
+    
+    /* Clear interrupts */
+    bool clearAmbientLightInt();
+    bool clearProximityInt();
+    bool clearAllInts();
+    
+    /* Proximity methods */
+    bool readProximity(uint16_t &val);
+
+    /* Ambient light methods */
+    bool readAmbientLightLux(float &val);
+    bool readAmbientLightLux(unsigned long &val);
+    float floatAmbientToLux(uint16_t Ch0, uint16_t Ch1);
+    unsigned long ulongAmbientToLux(uint16_t Ch0, uint16_t Ch1);
+    bool readCh0Light(uint16_t &val);
+    bool readCh1Light(uint16_t &val);
+    
+//private:
+
+    /* Proximity Interrupt Threshold */
+    uint16_t getProximityIntLowThreshold();
+    bool setProximityIntLowThreshold(uint16_t threshold);
+    uint16_t getProximityIntHighThreshold();
+    bool setProximityIntHighThreshold(uint16_t threshold);
+    
+    /* Raw I2C Commands */
+    bool wireWriteByte(uint8_t val);
+    bool wireWriteDataByte(uint8_t reg, uint8_t val);
+    bool wireWriteDataBlock(uint8_t reg, uint8_t *val, unsigned int len);
+    bool wireReadDataByte(uint8_t reg, uint8_t &val);
+    int wireReadDataBlock(uint8_t reg, uint8_t *val, unsigned int len);
+};
+
+#endif

+ 2208 - 0
licb/SparkFun_APDS9960.cpp

@@ -0,0 +1,2208 @@
+/**
+ * @file    SparkFun_APDS-9960.cpp
+ * @brief   Library for the SparkFun APDS-9960 breakout board
+ * @author  Shawn Hymel (SparkFun Electronics)
+ *
+ * @copyright	This code is public domain but you buy me a beer if you use
+ * this and we meet someday (Beerware license).
+ *
+ * This library interfaces the Avago APDS-9960 to Arduino over I2C. The library
+ * relies on the Arduino Wire (I2C) library. to use the library, instantiate an
+ * APDS9960 object, call init(), and call the appropriate functions.
+ *
+ * APDS-9960 current draw tests (default parameters):
+ *   Off:                   1mA
+ *   Waiting for gesture:   14mA
+ *   Gesture in progress:   35mA
+ */
+
+ #include <Arduino.h>
+ #include <Wire.h>
+
+ #include "SparkFun_APDS9960.h"
+
+/**
+ * @brief Constructor - Instantiates SparkFun_APDS9960 object
+ */
+SparkFun_APDS9960::SparkFun_APDS9960()
+{
+    gesture_ud_delta_ = 0;
+    gesture_lr_delta_ = 0;
+
+    gesture_ud_count_ = 0;
+    gesture_lr_count_ = 0;
+
+    gesture_near_count_ = 0;
+    gesture_far_count_ = 0;
+
+    gesture_state_ = 0;
+    gesture_motion_ = DIR_NONE;
+}
+
+/**
+ * @brief Destructor
+ */
+SparkFun_APDS9960::~SparkFun_APDS9960()
+{
+
+}
+
+/**
+ * @brief Configures I2C communications and initializes registers to defaults
+ *
+ * @return True if initialized successfully. False otherwise.
+ */
+bool SparkFun_APDS9960::init()
+{
+    uint8_t id;
+
+    /* Initialize I2C */
+    Wire.begin();
+
+    /* Read ID register and check against known values for APDS-9960 */
+    if( !wireReadDataByte(APDS9960_ID, id) ) {
+        return false;
+    }
+    if( !(id == APDS9960_ID_1 || id == APDS9960_ID_2 || id == APDS9960_ID_3) ) {
+        return false;
+    }
+
+    /* Set ENABLE register to 0 (disable all features) */
+    if( !setMode(ALL, OFF) ) {
+        return false;
+    }
+
+    /* Set default values for ambient light and proximity registers */
+    if( !wireWriteDataByte(APDS9960_ATIME, DEFAULT_ATIME) ) {
+        return false;
+    }
+    if( !wireWriteDataByte(APDS9960_WTIME, DEFAULT_WTIME) ) {
+        return false;
+    }
+    if( !wireWriteDataByte(APDS9960_PPULSE, DEFAULT_PROX_PPULSE) ) {
+        return false;
+    }
+    if( !wireWriteDataByte(APDS9960_POFFSET_UR, DEFAULT_POFFSET_UR) ) {
+        return false;
+    }
+    if( !wireWriteDataByte(APDS9960_POFFSET_DL, DEFAULT_POFFSET_DL) ) {
+        return false;
+    }
+    if( !wireWriteDataByte(APDS9960_CONFIG1, DEFAULT_CONFIG1) ) {
+        return false;
+    }
+    if( !setLEDDrive(DEFAULT_LDRIVE) ) {
+        return false;
+    }
+    if( !setProximityGain(DEFAULT_PGAIN) ) {
+        return false;
+    }
+    if( !setAmbientLightGain(DEFAULT_AGAIN) ) {
+        return false;
+    }
+    if( !setProxIntLowThresh(DEFAULT_PILT) ) {
+        return false;
+    }
+    if( !setProxIntHighThresh(DEFAULT_PIHT) ) {
+        return false;
+    }
+    if( !setLightIntLowThreshold(DEFAULT_AILT) ) {
+        return false;
+    }
+    if( !setLightIntHighThreshold(DEFAULT_AIHT) ) {
+        return false;
+    }
+    if( !wireWriteDataByte(APDS9960_PERS, DEFAULT_PERS) ) {
+        return false;
+    }
+    if( !wireWriteDataByte(APDS9960_CONFIG2, DEFAULT_CONFIG2) ) {
+        return false;
+    }
+    if( !wireWriteDataByte(APDS9960_CONFIG3, DEFAULT_CONFIG3) ) {
+        return false;
+    }
+
+    /* Set default values for gesture sense registers */
+    if( !setGestureEnterThresh(DEFAULT_GPENTH) ) {
+        return false;
+    }
+    if( !setGestureExitThresh(DEFAULT_GEXTH) ) {
+        return false;
+    }
+    if( !wireWriteDataByte(APDS9960_GCONF1, DEFAULT_GCONF1) ) {
+        return false;
+    }
+    if( !setGestureGain(DEFAULT_GGAIN) ) {
+        return false;
+    }
+    if( !setGestureLEDDrive(DEFAULT_GLDRIVE) ) {
+        return false;
+    }
+    if( !setGestureWaitTime(DEFAULT_GWTIME) ) {
+        return false;
+    }
+    if( !wireWriteDataByte(APDS9960_GOFFSET_U, DEFAULT_GOFFSET) ) {
+        return false;
+    }
+    if( !wireWriteDataByte(APDS9960_GOFFSET_D, DEFAULT_GOFFSET) ) {
+        return false;
+    }
+    if( !wireWriteDataByte(APDS9960_GOFFSET_L, DEFAULT_GOFFSET) ) {
+        return false;
+    }
+    if( !wireWriteDataByte(APDS9960_GOFFSET_R, DEFAULT_GOFFSET) ) {
+        return false;
+    }
+    if( !wireWriteDataByte(APDS9960_GPULSE, DEFAULT_GPULSE) ) {
+        return false;
+    }
+    if( !wireWriteDataByte(APDS9960_GCONF3, DEFAULT_GCONF3) ) {
+        return false;
+    }
+    if( !setGestureIntEnable(DEFAULT_GIEN) ) {
+        return false;
+    }
+
+#if 0
+    /* Gesture config register dump */
+    uint8_t reg;
+    uint8_t val;
+
+    for(reg = 0x80; reg <= 0xAF; reg++) {
+        if( (reg != 0x82) && \
+            (reg != 0x8A) && \
+            (reg != 0x91) && \
+            (reg != 0xA8) && \
+            (reg != 0xAC) && \
+            (reg != 0xAD) )
+        {
+            wireReadDataByte(reg, val);
+            Serial.print(reg, HEX);
+            Serial.print(": 0x");
+            Serial.println(val, HEX);
+        }
+    }
+
+    for(reg = 0xE4; reg <= 0xE7; reg++) {
+        wireReadDataByte(reg, val);
+        Serial.print(reg, HEX);
+        Serial.print(": 0x");
+        Serial.println(val, HEX);
+    }
+#endif
+
+    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 SparkFun_APDS9960::getMode()
+{
+    uint8_t enable_value;
+
+    /* Read current ENABLE register */
+    if( !wireReadDataByte(APDS9960_ENABLE, enable_value) ) {
+        return ERROR;
+    }
+
+    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)
+ * @return True if operation success. False otherwise.
+ */
+bool SparkFun_APDS9960::setMode(uint8_t mode, uint8_t enable)
+{
+    uint8_t reg_val;
+
+    /* Read current ENABLE register */
+    reg_val = getMode();
+    if( reg_val == ERROR ) {
+        return false;
+    }
+
+    /* Change bit(s) in ENABLE register */
+    enable = enable & 0x01;
+    if(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 */
+    if( !wireWriteDataByte(APDS9960_ENABLE, reg_val) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @brief Starts the light (R/G/B/Ambient) sensor on the APDS-9960
+ *
+ * @param[in] interrupts true to enable hardware interrupt on high or low light
+ * @return True if sensor enabled correctly. False on error.
+ */
+bool SparkFun_APDS9960::enableLightSensor(bool interrupts)
+{
+
+    /* Set default gain, interrupts, enable power, and enable sensor */
+    if( !setAmbientLightGain(DEFAULT_AGAIN) ) {
+        return false;
+    }
+    if( interrupts ) {
+        if( !setAmbientLightIntEnable(1) ) {
+            return false;
+        }
+    } else {
+        if( !setAmbientLightIntEnable(0) ) {
+            return false;
+        }
+    }
+    if( !enablePower() ){
+        return false;
+    }
+    if( !setMode(AMBIENT_LIGHT, 1) ) {
+        return false;
+    }
+
+    return true;
+
+}
+
+/**
+ * @brief Ends the light sensor on the APDS-9960
+ *
+ * @return True if sensor disabled correctly. False on error.
+ */
+bool SparkFun_APDS9960::disableLightSensor()
+{
+    if( !setAmbientLightIntEnable(0) ) {
+        return false;
+    }
+    if( !setMode(AMBIENT_LIGHT, 0) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @brief Starts the proximity sensor on the APDS-9960
+ *
+ * @param[in] interrupts true to enable hardware external interrupt on proximity
+ * @return True if sensor enabled correctly. False on error.
+ */
+bool SparkFun_APDS9960::enableProximitySensor(bool interrupts)
+{
+    /* Set default gain, LED, interrupts, enable power, and enable sensor */
+    if( !setProximityGain(DEFAULT_PGAIN) ) {
+        return false;
+    }
+    if( !setLEDDrive(DEFAULT_LDRIVE) ) {
+        return false;
+    }
+    if( interrupts ) {
+        if( !setProximityIntEnable(1) ) {
+            return false;
+        }
+    } else {
+        if( !setProximityIntEnable(0) ) {
+            return false;
+        }
+    }
+    if( !enablePower() ){
+        return false;
+    }
+    if( !setMode(PROXIMITY, 1) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @brief Ends the proximity sensor on the APDS-9960
+ *
+ * @return True if sensor disabled correctly. False on error.
+ */
+bool SparkFun_APDS9960::disableProximitySensor()
+{
+	if( !setProximityIntEnable(0) ) {
+		return false;
+	}
+	if( !setMode(PROXIMITY, 0) ) {
+		return false;
+	}
+
+	return true;
+}
+
+/**
+ * @brief Starts the gesture recognition engine on the APDS-9960
+ *
+ * @param[in] interrupts true to enable hardware external interrupt on gesture
+ * @return True if engine enabled correctly. False on error.
+ */
+bool SparkFun_APDS9960::enableGestureSensor(bool interrupts)
+{
+
+    /* 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();
+    if( !wireWriteDataByte(APDS9960_WTIME, 0xFF) ) {
+        return false;
+    }
+    if( !wireWriteDataByte(APDS9960_PPULSE, DEFAULT_GESTURE_PPULSE) ) {
+        return false;
+    }
+    if( !setLEDBoost(LED_BOOST_300) ) {
+        return false;
+    }
+    if( interrupts ) {
+        if( !setGestureIntEnable(1) ) {
+            return false;
+        }
+    } else {
+        if( !setGestureIntEnable(0) ) {
+            return false;
+        }
+    }
+    if( !setGestureMode(1) ) {
+        return false;
+    }
+    if( !enablePower() ){
+        return false;
+    }
+    if( !setMode(WAIT, 1) ) {
+        return false;
+    }
+    if( !setMode(PROXIMITY, 1) ) {
+        return false;
+    }
+    if( !setMode(GESTURE, 1) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @brief Ends the gesture recognition engine on the APDS-9960
+ *
+ * @return True if engine disabled correctly. False on error.
+ */
+bool SparkFun_APDS9960::disableGestureSensor()
+{
+    resetGestureParameters();
+    if( !setGestureIntEnable(0) ) {
+        return false;
+    }
+    if( !setGestureMode(0) ) {
+        return false;
+    }
+    if( !setMode(GESTURE, 0) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @brief Determines if there is a gesture available for reading
+ *
+ * @return True if gesture available. False otherwise.
+ */
+bool SparkFun_APDS9960::isGestureAvailable()
+{
+    uint8_t val;
+
+    /* Read value from GSTATUS register */
+    if( !wireReadDataByte(APDS9960_GSTATUS, val) ) {
+        return ERROR;
+    }
+
+    /* 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.
+ */
+int SparkFun_APDS9960::readGesture()
+{
+    uint8_t fifo_level = 0;
+    uint8_t fifo_data[128];
+    uint8_t gstatus;
+    int bytes_read = 0;
+    int motion;
+    int i;
+
+    /* 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) {
+
+        /* 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? */
+        if( !wireReadDataByte(APDS9960_GSTATUS, gstatus) ) {
+            return ERROR;
+        }
+
+        /* If we have valid data, read in FIFO */
+        if( (gstatus & APDS9960_GVALID) == APDS9960_GVALID ) {
+
+            /* Read the current FIFO level */
+            if( !wireReadDataByte(APDS9960_GFLVL, fifo_level) ) {
+                return ERROR;
+            }
+
+#if DEBUG
+            Serial.print("FIFO Level: ");
+            Serial.println(fifo_level);
+#endif
+
+            /* 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 DEBUG
+                Serial.print("FIFO Dump: ");
+                for ( i = 0; i < bytes_read; i++ ) {
+                    Serial.print(fifo_data[i]);
+                    Serial.print(" ");
+                }
+                Serial.println();
+#endif
+
+                /* 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++;
+                    }
+
+#if DEBUG
+                Serial.print("Up Data: ");
+                for ( i = 0; i < gesture_data_.total_gestures; i++ ) {
+                    Serial.print(gesture_data_.u_data[i]);
+                    Serial.print(" ");
+                }
+                Serial.println();
+#endif
+
+                    /* Filter and process gesture data. Decode near/far state */
+                    if( processGestureData() ) {
+                        if( decodeGesture() ) {
+                            //***TODO: U-Turn Gestures
+#if DEBUG
+                            //Serial.println(gesture_motion_);
+#endif
+                        }
+                    }
+
+                    /* 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_;
+#if DEBUG
+            Serial.print("END: ");
+            Serial.println(gesture_motion_);
+#endif
+            resetGestureParameters();
+            return motion;
+        }
+    }
+}
+
+/**
+ * Turn the APDS-9960 on
+ *
+ * @return True if operation successful. False otherwise.
+ */
+bool SparkFun_APDS9960::enablePower()
+{
+    if( !setMode(POWER, 1) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * Turn the APDS-9960 off
+ *
+ * @return True if operation successful. False otherwise.
+ */
+bool SparkFun_APDS9960::disablePower()
+{
+    if( !setMode(POWER, 0) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/*******************************************************************************
+ * Ambient light and color sensor controls
+ ******************************************************************************/
+
+/**
+ * @brief Reads the ambient (clear) light level as a 16-bit value
+ *
+ * @param[out] val value of the light sensor.
+ * @return True if operation successful. False otherwise.
+ */
+bool SparkFun_APDS9960::readAmbientLight(uint16_t &val)
+{
+    uint8_t val_byte;
+    val = 0;
+
+    /* Read value from clear channel, low byte register */
+    if( !wireReadDataByte(APDS9960_CDATAL, val_byte) ) {
+        return false;
+    }
+    val = val_byte;
+
+    /* Read value from clear channel, high byte register */
+    if( !wireReadDataByte(APDS9960_CDATAH, val_byte) ) {
+        return false;
+    }
+    val = val + ((uint16_t)val_byte << 8);
+
+    return true;
+}
+
+/**
+ * @brief Reads the red light level as a 16-bit value
+ *
+ * @param[out] val value of the light sensor.
+ * @return True if operation successful. False otherwise.
+ */
+bool SparkFun_APDS9960::readRedLight(uint16_t &val)
+{
+    uint8_t val_byte;
+    val = 0;
+
+    /* Read value from clear channel, low byte register */
+    if( !wireReadDataByte(APDS9960_RDATAL, val_byte) ) {
+        return false;
+    }
+    val = val_byte;
+
+    /* Read value from clear channel, high byte register */
+    if( !wireReadDataByte(APDS9960_RDATAH, val_byte) ) {
+        return false;
+    }
+    val = val + ((uint16_t)val_byte << 8);
+
+    return true;
+}
+
+/**
+ * @brief Reads the green light level as a 16-bit value
+ *
+ * @param[out] val value of the light sensor.
+ * @return True if operation successful. False otherwise.
+ */
+bool SparkFun_APDS9960::readGreenLight(uint16_t &val)
+{
+    uint8_t val_byte;
+    val = 0;
+
+    /* Read value from clear channel, low byte register */
+    if( !wireReadDataByte(APDS9960_GDATAL, val_byte) ) {
+        return false;
+    }
+    val = val_byte;
+
+    /* Read value from clear channel, high byte register */
+    if( !wireReadDataByte(APDS9960_GDATAH, val_byte) ) {
+        return false;
+    }
+    val = val + ((uint16_t)val_byte << 8);
+
+    return true;
+}
+
+/**
+ * @brief Reads the red light level as a 16-bit value
+ *
+ * @param[out] val value of the light sensor.
+ * @return True if operation successful. False otherwise.
+ */
+bool SparkFun_APDS9960::readBlueLight(uint16_t &val)
+{
+    uint8_t val_byte;
+    val = 0;
+
+    /* Read value from clear channel, low byte register */
+    if( !wireReadDataByte(APDS9960_BDATAL, val_byte) ) {
+        return false;
+    }
+    val = val_byte;
+
+    /* Read value from clear channel, high byte register */
+    if( !wireReadDataByte(APDS9960_BDATAH, val_byte) ) {
+        return false;
+    }
+    val = val + ((uint16_t)val_byte << 8);
+
+    return true;
+}
+
+/*******************************************************************************
+ * Proximity sensor controls
+ ******************************************************************************/
+
+/**
+ * @brief Reads the proximity level as an 8-bit value
+ *
+ * @param[out] val value of the proximity sensor.
+ * @return True if operation successful. False otherwise.
+ */
+bool SparkFun_APDS9960::readProximity(uint8_t &val)
+{
+    val = 0;
+
+    /* Read value from proximity data register */
+    if( !wireReadDataByte(APDS9960_PDATA, val) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/*******************************************************************************
+ * High-level gesture controls
+ ******************************************************************************/
+
+/**
+ * @brief Resets all the parameters in the gesture data member
+ */
+void SparkFun_APDS9960::resetGestureParameters()
+{
+    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_near_count_ = 0;
+    gesture_far_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 SparkFun_APDS9960::processGestureData()
+{
+    uint8_t u_first = 1;
+    uint8_t d_first = 1;
+    uint8_t l_first = 1;
+    uint8_t r_first = 1;
+    uint8_t u_last = 1;
+    uint8_t d_last = 1;
+    uint8_t l_last = 1;
+    uint8_t r_last = 1;
+    int ud_ratio_first;
+    int lr_ratio_first;
+    int ud_ratio_last;
+    int lr_ratio_last;
+    int ud_delta;
+    int lr_delta;
+    int 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;
+            }
+        }
+
+        /* Find the last value in U/D/L/R above the threshold */
+        for( i = gesture_data_.total_gestures - 1; i >= 0; i-- ) {
+#if DEBUG
+            Serial.print(F("Finding last: "));
+            Serial.print(F("U:"));
+            Serial.print(gesture_data_.u_data[i]);
+            Serial.print(F(" D:"));
+            Serial.print(gesture_data_.d_data[i]);
+            Serial.print(F(" L:"));
+            Serial.print(gesture_data_.l_data[i]);
+            Serial.print(F(" R:"));
+            Serial.println(gesture_data_.r_data[i]);
+#endif
+            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);
+
+#if DEBUG
+    Serial.print(F("Last Values: "));
+    Serial.print(F("U:"));
+    Serial.print(u_last);
+    Serial.print(F(" D:"));
+    Serial.print(d_last);
+    Serial.print(F(" L:"));
+    Serial.print(l_last);
+    Serial.print(F(" R:"));
+    Serial.println(r_last);
+
+    Serial.print(F("Ratios: "));
+    Serial.print(F("UD Fi: "));
+    Serial.print(ud_ratio_first);
+    Serial.print(F(" UD La: "));
+    Serial.print(ud_ratio_last);
+    Serial.print(F(" LR Fi: "));
+    Serial.print(lr_ratio_first);
+    Serial.print(F(" LR La: "));
+    Serial.println(lr_ratio_last);
+#endif
+
+    /* 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;
+
+#if DEBUG
+    Serial.print("Deltas: ");
+    Serial.print("UD: ");
+    Serial.print(ud_delta);
+    Serial.print(" LR: ");
+    Serial.println(lr_delta);
+#endif
+
+    /* Accumulate the UD and LR delta values */
+    gesture_ud_delta_ += ud_delta;
+    gesture_lr_delta_ += lr_delta;
+
+#if DEBUG
+    Serial.print("Accumulations: ");
+    Serial.print("UD: ");
+    Serial.print(gesture_ud_delta_);
+    Serial.print(" LR: ");
+    Serial.println(gesture_lr_delta_);
+#endif
+
+    /* 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;
+    }
+
+    /* Determine Near/Far gesture */
+    if( (gesture_ud_count_ == 0) && (gesture_lr_count_ == 0) ) {
+        if( (abs(ud_delta) < GESTURE_SENSITIVITY_2) && \
+            (abs(lr_delta) < GESTURE_SENSITIVITY_2) ) {
+
+            if( (ud_delta == 0) && (lr_delta == 0) ) {
+                gesture_near_count_++;
+            } else if( (ud_delta != 0) || (lr_delta != 0) ) {
+                gesture_far_count_++;
+            }
+
+            if( (gesture_near_count_ >= 10) && (gesture_far_count_ >= 2) ) {
+                if( (ud_delta == 0) && (lr_delta == 0) ) {
+                    gesture_state_ = NEAR_STATE;
+                } else if( (ud_delta != 0) && (lr_delta != 0) ) {
+                    gesture_state_ = FAR_STATE;
+                }
+                return true;
+            }
+        }
+    } else {
+        if( (abs(ud_delta) < GESTURE_SENSITIVITY_2) && \
+            (abs(lr_delta) < GESTURE_SENSITIVITY_2) ) {
+
+            if( (ud_delta == 0) && (lr_delta == 0) ) {
+                gesture_near_count_++;
+            }
+
+            if( gesture_near_count_ >= 10 ) {
+                gesture_ud_count_ = 0;
+                gesture_lr_count_ = 0;
+                gesture_ud_delta_ = 0;
+                gesture_lr_delta_ = 0;
+            }
+        }
+    }
+
+#if DEBUG
+    Serial.print("UD_CT: ");
+    Serial.print(gesture_ud_count_);
+    Serial.print(" LR_CT: ");
+    Serial.print(gesture_lr_count_);
+    Serial.print(" NEAR_CT: ");
+    Serial.print(gesture_near_count_);
+    Serial.print(" FAR_CT: ");
+    Serial.println(gesture_far_count_);
+    Serial.println("----------");
+#endif
+
+    return false;
+}
+
+/**
+ * @brief Determines swipe direction or near/far state
+ *
+ * @return True if near/far event. False otherwise.
+ */
+bool SparkFun_APDS9960::decodeGesture()
+{
+    /* Return if near or far event is detected */
+    if( gesture_state_ == NEAR_STATE ) {
+        gesture_motion_ = DIR_NEAR;
+        return true;
+    } else if ( gesture_state_ == FAR_STATE ) {
+        gesture_motion_ = DIR_FAR;
+        return true;
+    }
+
+    /* 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;
+}
+
+/*******************************************************************************
+ * Getters and setters for register values
+ ******************************************************************************/
+
+/**
+ * @brief Returns the lower threshold for proximity detection
+ *
+ * @return lower threshold
+ */
+uint8_t SparkFun_APDS9960::getProxIntLowThresh()
+{
+    uint8_t val;
+
+    /* Read value from PILT register */
+    if( !wireReadDataByte(APDS9960_PILT, val) ) {
+        val = 0;
+    }
+
+    return val;
+}
+
+/**
+ * @brief Sets the lower threshold for proximity detection
+ *
+ * @param[in] threshold the lower proximity threshold
+ * @return True if operation successful. False otherwise.
+ */
+bool SparkFun_APDS9960::setProxIntLowThresh(uint8_t threshold)
+{
+    if( !wireWriteDataByte(APDS9960_PILT, threshold) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @brief Returns the high threshold for proximity detection
+ *
+ * @return high threshold
+ */
+uint8_t SparkFun_APDS9960::getProxIntHighThresh()
+{
+    uint8_t val;
+
+    /* Read value from PIHT register */
+    if( !wireReadDataByte(APDS9960_PIHT, val) ) {
+        val = 0;
+    }
+
+    return val;
+}
+
+/**
+ * @brief Sets the high threshold for proximity detection
+ *
+ * @param[in] threshold the high proximity threshold
+ * @return True if operation successful. False otherwise.
+ */
+bool SparkFun_APDS9960::setProxIntHighThresh(uint8_t threshold)
+{
+    if( !wireWriteDataByte(APDS9960_PIHT, threshold) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @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 SparkFun_APDS9960::getLEDDrive()
+{
+    uint8_t val;
+
+    /* Read value from CONTROL register */
+    if( !wireReadDataByte(APDS9960_CONTROL, val) ) {
+        return ERROR;
+    }
+
+    /* 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
+ * @return True if operation successful. False otherwise.
+ */
+bool SparkFun_APDS9960::setLEDDrive(uint8_t drive)
+{
+    uint8_t val;
+
+    /* Read value from CONTROL register */
+    if( !wireReadDataByte(APDS9960_CONTROL, val) ) {
+        return false;
+    }
+
+    /* Set bits in register to given value */
+    drive &= 0b00000011;
+    drive = drive << 6;
+    val &= 0b00111111;
+    val |= drive;
+
+    /* Write register value back into CONTROL register */
+    if( !wireWriteDataByte(APDS9960_CONTROL, val) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @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 SparkFun_APDS9960::getProximityGain()
+{
+    uint8_t val;
+
+    /* Read value from CONTROL register */
+    if( !wireReadDataByte(APDS9960_CONTROL, val) ) {
+        return ERROR;
+    }
+
+    /* 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
+ * @return True if operation successful. False otherwise.
+ */
+bool SparkFun_APDS9960::setProximityGain(uint8_t drive)
+{
+    uint8_t val;
+
+    /* Read value from CONTROL register */
+    if( !wireReadDataByte(APDS9960_CONTROL, val) ) {
+        return false;
+    }
+
+    /* Set bits in register to given value */
+    drive &= 0b00000011;
+    drive = drive << 2;
+    val &= 0b11110011;
+    val |= drive;
+
+    /* Write register value back into CONTROL register */
+    if( !wireWriteDataByte(APDS9960_CONTROL, val) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @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.
+ */
+uint8_t SparkFun_APDS9960::getAmbientLightGain()
+{
+    uint8_t val;
+
+    /* Read value from CONTROL register */
+    if( !wireReadDataByte(APDS9960_CONTROL, val) ) {
+        return ERROR;
+    }
+
+    /* Shift and mask out ADRIVE bits */
+    val &= 0b00000011;
+
+    return val;
+}
+
+/**
+ * @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
+ * @return True if operation successful. False otherwise.
+ */
+bool SparkFun_APDS9960::setAmbientLightGain(uint8_t drive)
+{
+    uint8_t val;
+
+    /* Read value from CONTROL register */
+    if( !wireReadDataByte(APDS9960_CONTROL, val) ) {
+        return false;
+    }
+
+    /* Set bits in register to given value */
+    drive &= 0b00000011;
+    val &= 0b11111100;
+    val |= drive;
+
+    /* Write register value back into CONTROL register */
+    if( !wireWriteDataByte(APDS9960_CONTROL, val) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @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 SparkFun_APDS9960::getLEDBoost()
+{
+    uint8_t val;
+
+    /* Read value from CONFIG2 register */
+    if( !wireReadDataByte(APDS9960_CONFIG2, val) ) {
+        return ERROR;
+    }
+
+    /* 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%)
+ * @return True if operation successful. False otherwise.
+ */
+bool SparkFun_APDS9960::setLEDBoost(uint8_t boost)
+{
+    uint8_t val;
+
+    /* Read value from CONFIG2 register */
+    if( !wireReadDataByte(APDS9960_CONFIG2, val) ) {
+        return false;
+    }
+
+    /* Set bits in register to given value */
+    boost &= 0b00000011;
+    boost = boost << 4;
+    val &= 0b11001111;
+    val |= boost;
+
+    /* Write register value back into CONFIG2 register */
+    if( !wireWriteDataByte(APDS9960_CONFIG2, val) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @brief Gets proximity gain compensation enable
+ *
+ * @return 1 if compensation is enabled. 0 if not. 0xFF on error.
+ */
+uint8_t SparkFun_APDS9960::getProxGainCompEnable()
+{
+    uint8_t val;
+
+    /* Read value from CONFIG3 register */
+    if( !wireReadDataByte(APDS9960_CONFIG3, val) ) {
+        return ERROR;
+    }
+
+    /* 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.
+ * @return True if operation successful. False otherwise.
+ */
+ bool SparkFun_APDS9960::setProxGainCompEnable(uint8_t enable)
+{
+    uint8_t val;
+
+    /* Read value from CONFIG3 register */
+    if( !wireReadDataByte(APDS9960_CONFIG3, val) ) {
+        return false;
+    }
+
+    /* Set bits in register to given value */
+    enable &= 0b00000001;
+    enable = enable << 5;
+    val &= 0b11011111;
+    val |= enable;
+
+    /* Write register value back into CONFIG3 register */
+    if( !wireWriteDataByte(APDS9960_CONFIG3, val) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @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 SparkFun_APDS9960::getProxPhotoMask()
+{
+    uint8_t val;
+
+    /* Read value from CONFIG3 register */
+    if( !wireReadDataByte(APDS9960_CONFIG3, val) ) {
+        return ERROR;
+    }
+
+    /* 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
+ * @return True if operation successful. False otherwise.
+ */
+bool SparkFun_APDS9960::setProxPhotoMask(uint8_t mask)
+{
+    uint8_t val;
+
+    /* Read value from CONFIG3 register */
+    if( !wireReadDataByte(APDS9960_CONFIG3, val) ) {
+        return false;
+    }
+
+    /* Set bits in register to given value */
+    mask &= 0b00001111;
+    val &= 0b11110000;
+    val |= mask;
+
+    /* Write register value back into CONFIG3 register */
+    if( !wireWriteDataByte(APDS9960_CONFIG3, val) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @brief Gets the entry proximity threshold for gesture sensing
+ *
+ * @return Current entry proximity threshold.
+ */
+uint8_t SparkFun_APDS9960::getGestureEnterThresh()
+{
+    uint8_t val;
+
+    /* Read value from GPENTH register */
+    if( !wireReadDataByte(APDS9960_GPENTH, val) ) {
+        val = 0;
+    }
+
+    return val;
+}
+
+/**
+ * @brief Sets the entry proximity threshold for gesture sensing
+ *
+ * @param[in] threshold proximity value needed to start gesture mode
+ * @return True if operation successful. False otherwise.
+ */
+bool SparkFun_APDS9960::setGestureEnterThresh(uint8_t threshold)
+{
+    if( !wireWriteDataByte(APDS9960_GPENTH, threshold) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @brief Gets the exit proximity threshold for gesture sensing
+ *
+ * @return Current exit proximity threshold.
+ */
+uint8_t SparkFun_APDS9960::getGestureExitThresh()
+{
+    uint8_t val;
+
+    /* Read value from GEXTH register */
+    if( !wireReadDataByte(APDS9960_GEXTH, val) ) {
+        val = 0;
+    }
+
+    return val;
+}
+
+/**
+ * @brief Sets the exit proximity threshold for gesture sensing
+ *
+ * @param[in] threshold proximity value needed to end gesture mode
+ * @return True if operation successful. False otherwise.
+ */
+bool SparkFun_APDS9960::setGestureExitThresh(uint8_t threshold)
+{
+    if( !wireWriteDataByte(APDS9960_GEXTH, threshold) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @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 SparkFun_APDS9960::getGestureGain()
+{
+    uint8_t val;
+
+    /* Read value from GCONF2 register */
+    if( !wireReadDataByte(APDS9960_GCONF2, val) ) {
+        return ERROR;
+    }
+
+    /* 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
+ * @return True if operation successful. False otherwise.
+ */
+bool SparkFun_APDS9960::setGestureGain(uint8_t gain)
+{
+    uint8_t val;
+
+    /* Read value from GCONF2 register */
+    if( !wireReadDataByte(APDS9960_GCONF2, val) ) {
+        return false;
+    }
+
+    /* Set bits in register to given value */
+    gain &= 0b00000011;
+    gain = gain << 5;
+    val &= 0b10011111;
+    val |= gain;
+
+    /* Write register value back into GCONF2 register */
+    if( !wireWriteDataByte(APDS9960_GCONF2, val) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @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 SparkFun_APDS9960::getGestureLEDDrive()
+{
+    uint8_t val;
+
+    /* Read value from GCONF2 register */
+    if( !wireReadDataByte(APDS9960_GCONF2, val) ) {
+        return ERROR;
+    }
+
+    /* 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
+ * @return True if operation successful. False otherwise.
+ */
+bool SparkFun_APDS9960::setGestureLEDDrive(uint8_t drive)
+{
+    uint8_t val;
+
+    /* Read value from GCONF2 register */
+    if( !wireReadDataByte(APDS9960_GCONF2, val) ) {
+        return false;
+    }
+
+    /* Set bits in register to given value */
+    drive &= 0b00000011;
+    drive = drive << 3;
+    val &= 0b11100111;
+    val |= drive;
+
+    /* Write register value back into GCONF2 register */
+    if( !wireWriteDataByte(APDS9960_GCONF2, val) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @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 SparkFun_APDS9960::getGestureWaitTime()
+{
+    uint8_t val;
+
+    /* Read value from GCONF2 register */
+    if( !wireReadDataByte(APDS9960_GCONF2, val) ) {
+        return ERROR;
+    }
+
+    /* 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
+ * @return True if operation successful. False otherwise.
+ */
+bool SparkFun_APDS9960::setGestureWaitTime(uint8_t time)
+{
+    uint8_t val;
+
+    /* Read value from GCONF2 register */
+    if( !wireReadDataByte(APDS9960_GCONF2, val) ) {
+        return false;
+    }
+
+    /* Set bits in register to given value */
+    time &= 0b00000111;
+    val &= 0b11111000;
+    val |= time;
+
+    /* Write register value back into GCONF2 register */
+    if( !wireWriteDataByte(APDS9960_GCONF2, val) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @brief Gets the low threshold for ambient light interrupts
+ *
+ * @param[out] threshold current low threshold stored on the APDS-9960
+ * @return True if operation successful. False otherwise.
+ */
+bool SparkFun_APDS9960::getLightIntLowThreshold(uint16_t &threshold)
+{
+    uint8_t val_byte;
+    threshold = 0;
+
+    /* Read value from ambient light low threshold, low byte register */
+    if( !wireReadDataByte(APDS9960_AILTL, val_byte) ) {
+        return false;
+    }
+    threshold = val_byte;
+
+    /* Read value from ambient light low threshold, high byte register */
+    if( !wireReadDataByte(APDS9960_AILTH, val_byte) ) {
+        return false;
+    }
+    threshold = threshold + ((uint16_t)val_byte << 8);
+
+    return true;
+}
+
+/**
+ * @brief Sets the low threshold for ambient light interrupts
+ *
+ * @param[in] threshold low threshold value for interrupt to trigger
+ * @return True if operation successful. False otherwise.
+ */
+bool SparkFun_APDS9960::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 */
+    if( !wireWriteDataByte(APDS9960_AILTL, val_low) ) {
+        return false;
+    }
+
+    /* Write high byte */
+    if( !wireWriteDataByte(APDS9960_AILTH, val_high) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @brief Gets the high threshold for ambient light interrupts
+ *
+ * @param[out] threshold current low threshold stored on the APDS-9960
+ * @return True if operation successful. False otherwise.
+ */
+bool SparkFun_APDS9960::getLightIntHighThreshold(uint16_t &threshold)
+{
+    uint8_t val_byte;
+    threshold = 0;
+
+    /* Read value from ambient light high threshold, low byte register */
+    if( !wireReadDataByte(APDS9960_AIHTL, val_byte) ) {
+        return false;
+    }
+    threshold = val_byte;
+
+    /* Read value from ambient light high threshold, high byte register */
+    if( !wireReadDataByte(APDS9960_AIHTH, val_byte) ) {
+        return false;
+    }
+    threshold = threshold + ((uint16_t)val_byte << 8);
+
+    return true;
+}
+
+/**
+ * @brief Sets the high threshold for ambient light interrupts
+ *
+ * @param[in] threshold high threshold value for interrupt to trigger
+ * @return True if operation successful. False otherwise.
+ */
+bool SparkFun_APDS9960::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 */
+    if( !wireWriteDataByte(APDS9960_AIHTL, val_low) ) {
+        return false;
+    }
+
+    /* Write high byte */
+    if( !wireWriteDataByte(APDS9960_AIHTH, val_high) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @brief Gets the low threshold for proximity interrupts
+ *
+ * @param[out] threshold current low threshold stored on the APDS-9960
+ * @return True if operation successful. False otherwise.
+ */
+bool SparkFun_APDS9960::getProximityIntLowThreshold(uint8_t &threshold)
+{
+    threshold = 0;
+
+    /* Read value from proximity low threshold register */
+    if( !wireReadDataByte(APDS9960_PILT, threshold) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @brief Sets the low threshold for proximity interrupts
+ *
+ * @param[in] threshold low threshold value for interrupt to trigger
+ * @return True if operation successful. False otherwise.
+ */
+bool SparkFun_APDS9960::setProximityIntLowThreshold(uint8_t threshold)
+{
+
+    /* Write threshold value to register */
+    if( !wireWriteDataByte(APDS9960_PILT, threshold) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @brief Gets the high threshold for proximity interrupts
+ *
+ * @param[out] threshold current low threshold stored on the APDS-9960
+ * @return True if operation successful. False otherwise.
+ */
+bool SparkFun_APDS9960::getProximityIntHighThreshold(uint8_t &threshold)
+{
+    threshold = 0;
+
+    /* Read value from proximity low threshold register */
+    if( !wireReadDataByte(APDS9960_PIHT, threshold) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @brief Sets the high threshold for proximity interrupts
+ *
+ * @param[in] threshold high threshold value for interrupt to trigger
+ * @return True if operation successful. False otherwise.
+ */
+bool SparkFun_APDS9960::setProximityIntHighThreshold(uint8_t threshold)
+{
+
+    /* Write threshold value to register */
+    if( !wireWriteDataByte(APDS9960_PIHT, threshold) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @brief Gets if ambient light interrupts are enabled or not
+ *
+ * @return 1 if interrupts are enabled, 0 if not. 0xFF on error.
+ */
+uint8_t SparkFun_APDS9960::getAmbientLightIntEnable()
+{
+    uint8_t val;
+
+    /* Read value from ENABLE register */
+    if( !wireReadDataByte(APDS9960_ENABLE, val) ) {
+        return ERROR;
+    }
+
+    /* 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
+ * @return True if operation successful. False otherwise.
+ */
+bool SparkFun_APDS9960::setAmbientLightIntEnable(uint8_t enable)
+{
+    uint8_t val;
+
+    /* Read value from ENABLE register */
+    if( !wireReadDataByte(APDS9960_ENABLE, val) ) {
+        return false;
+    }
+
+    /* Set bits in register to given value */
+    enable &= 0b00000001;
+    enable = enable << 4;
+    val &= 0b11101111;
+    val |= enable;
+
+    /* Write register value back into ENABLE register */
+    if( !wireWriteDataByte(APDS9960_ENABLE, val) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @brief Gets if proximity interrupts are enabled or not
+ *
+ * @return 1 if interrupts are enabled, 0 if not. 0xFF on error.
+ */
+uint8_t SparkFun_APDS9960::getProximityIntEnable()
+{
+    uint8_t val;
+
+    /* Read value from ENABLE register */
+    if( !wireReadDataByte(APDS9960_ENABLE, val) ) {
+        return ERROR;
+    }
+
+    /* 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
+ * @return True if operation successful. False otherwise.
+ */
+bool SparkFun_APDS9960::setProximityIntEnable(uint8_t enable)
+{
+    uint8_t val;
+
+    /* Read value from ENABLE register */
+    if( !wireReadDataByte(APDS9960_ENABLE, val) ) {
+        return false;
+    }
+
+    /* Set bits in register to given value */
+    enable &= 0b00000001;
+    enable = enable << 5;
+    val &= 0b11011111;
+    val |= enable;
+
+    /* Write register value back into ENABLE register */
+    if( !wireWriteDataByte(APDS9960_ENABLE, val) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @brief Gets if gesture interrupts are enabled or not
+ *
+ * @return 1 if interrupts are enabled, 0 if not. 0xFF on error.
+ */
+uint8_t SparkFun_APDS9960::getGestureIntEnable()
+{
+    uint8_t val;
+
+    /* Read value from GCONF4 register */
+    if( !wireReadDataByte(APDS9960_GCONF4, val) ) {
+        return ERROR;
+    }
+
+    /* 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
+ * @return True if operation successful. False otherwise.
+ */
+bool SparkFun_APDS9960::setGestureIntEnable(uint8_t enable)
+{
+    uint8_t val;
+
+    /* Read value from GCONF4 register */
+    if( !wireReadDataByte(APDS9960_GCONF4, val) ) {
+        return false;
+    }
+
+    /* Set bits in register to given value */
+    enable &= 0b00000001;
+    enable = enable << 1;
+    val &= 0b11111101;
+    val |= enable;
+
+    /* Write register value back into GCONF4 register */
+    if( !wireWriteDataByte(APDS9960_GCONF4, val) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @brief Clears the ambient light interrupt
+ *
+ * @return True if operation completed successfully. False otherwise.
+ */
+bool SparkFun_APDS9960::clearAmbientLightInt()
+{
+    uint8_t throwaway;
+    if( !wireReadDataByte(APDS9960_AICLEAR, throwaway) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @brief Clears the proximity interrupt
+ *
+ * @return True if operation completed successfully. False otherwise.
+ */
+bool SparkFun_APDS9960::clearProximityInt()
+{
+    uint8_t throwaway;
+    if( !wireReadDataByte(APDS9960_PICLEAR, throwaway) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @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 SparkFun_APDS9960::getGestureMode()
+{
+    uint8_t val;
+
+    /* Read value from GCONF4 register */
+    if( !wireReadDataByte(APDS9960_GCONF4, val) ) {
+        return ERROR;
+    }
+
+    /* 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.
+ * @return True if operation successful. False otherwise.
+ */
+bool SparkFun_APDS9960::setGestureMode(uint8_t mode)
+{
+    uint8_t val;
+
+    /* Read value from GCONF4 register */
+    if( !wireReadDataByte(APDS9960_GCONF4, val) ) {
+        return false;
+    }
+
+    /* Set bits in register to given value */
+    mode &= 0b00000001;
+    val &= 0b11111110;
+    val |= mode;
+
+    /* Write register value back into GCONF4 register */
+    if( !wireWriteDataByte(APDS9960_GCONF4, val) ) {
+        return false;
+    }
+
+    return true;
+}
+
+/*******************************************************************************
+ * Raw I2C Reads and Writes
+ ******************************************************************************/
+
+/**
+ * @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 SparkFun_APDS9960::wireWriteByte(uint8_t val)
+{
+    Wire.beginTransmission(APDS9960_I2C_ADDR);
+    Wire.write(val);
+    if( Wire.endTransmission() != 0 ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @brief Writes a single byte to the I2C device and specified register
+ *
+ * @param[in] reg the register in the I2C device to write to
+ * @param[in] val the 1-byte value to write to the I2C device
+ * @return True if successful write operation. False otherwise.
+ */
+bool SparkFun_APDS9960::wireWriteDataByte(uint8_t reg, uint8_t val)
+{
+    Wire.beginTransmission(APDS9960_I2C_ADDR);
+    Wire.write(reg);
+    Wire.write(val);
+    if( Wire.endTransmission() != 0 ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @brief Writes a block (array) of bytes to the I2C device and register
+ *
+ * @param[in] reg the register in the I2C device to write to
+ * @param[in] val pointer to the beginning of the data byte array
+ * @param[in] len the length (in bytes) of the data to write
+ * @return True if successful write operation. False otherwise.
+ */
+bool SparkFun_APDS9960::wireWriteDataBlock(  uint8_t reg,
+                                        uint8_t *val,
+                                        unsigned int len)
+{
+    unsigned int i;
+
+    Wire.beginTransmission(APDS9960_I2C_ADDR);
+    Wire.write(reg);
+    for(i = 0; i < len; i++) {
+        Wire.write(val[i]);
+    }
+    if( Wire.endTransmission() != 0 ) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @brief Reads a single byte from the I2C device and specified register
+ *
+ * @param[in] reg the register to read from
+ * @param[out] the value returned from the register
+ * @return True if successful read operation. False otherwise.
+ */
+bool SparkFun_APDS9960::wireReadDataByte(uint8_t reg, uint8_t &val)
+{
+
+    /* Indicate which register we want to read from */
+    if (!wireWriteByte(reg)) {
+        return false;
+    }
+
+    /* Read from register */
+    Wire.requestFrom(APDS9960_I2C_ADDR, 1);
+    while (Wire.available()) {
+        val = Wire.read();
+    }
+
+    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.
+ */
+int SparkFun_APDS9960::wireReadDataBlock(   uint8_t reg,
+                                        uint8_t *val,
+                                        unsigned int 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;
+}

+ 347 - 0
licb/SparkFun_APDS9960.h

@@ -0,0 +1,347 @@
+/**
+ * @file    SparkFun_APDS-9960.h
+ * @brief   Library for the SparkFun APDS-9960 breakout board
+ * @author  Shawn Hymel (SparkFun Electronics)
+ *
+ * @copyright	This code is public domain but you buy me a beer if you use
+ * this and we meet someday (Beerware license).
+ *
+ * This library interfaces the Avago APDS-9960 to Arduino over I2C. The library
+ * relies on the Arduino Wire (I2C) library. to use the library, instantiate an
+ * APDS9960 object, call init(), and call the appropriate functions.
+ */
+ 
+#ifndef SparkFun_APDS9960_H
+#define SparkFun_APDS9960_H
+
+#include <Arduino.h>
+
+/* Debug */
+#define DEBUG                   0
+
+/* APDS-9960 I2C address */
+#define APDS9960_I2C_ADDR       0x39
+
+/* Gesture parameters */
+#define GESTURE_THRESHOLD_OUT   10
+#define GESTURE_SENSITIVITY_1   50
+#define GESTURE_SENSITIVITY_2   20
+
+/* Error code for returned values */
+#define ERROR                   0xFF
+
+/* Acceptable device IDs */
+#define APDS9960_ID_1           0xAB
+#define APDS9960_ID_2           0x9C 
+#define APDS9960_ID_3           0xA8 
+
+/* 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           219     // 103ms
+#define DEFAULT_WTIME           246     // 27ms
+#define DEFAULT_PROX_PPULSE     0x87    // 16us, 8 pulses
+#define DEFAULT_GESTURE_PPULSE  0x89    // 16us, 10 pulses
+#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
+#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
+#define DEFAULT_GWTIME          GWTIME_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
+
+/* Direction definitions */
+enum {
+  DIR_NONE,
+  DIR_LEFT,
+  DIR_RIGHT,
+  DIR_UP,
+  DIR_DOWN,
+  DIR_NEAR,
+  DIR_FAR,
+  DIR_ALL
+};
+
+/* State definitions */
+enum {
+  NA_STATE,
+  NEAR_STATE,
+  FAR_STATE,
+  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;
+
+/* APDS9960 Class */
+class SparkFun_APDS9960 {
+public:
+
+    /* Initialization methods */
+    SparkFun_APDS9960();
+    ~SparkFun_APDS9960();
+    bool init();
+    uint8_t getMode();
+    bool setMode(uint8_t mode, uint8_t enable);
+    
+    /* Turn the APDS-9960 on and off */
+    bool enablePower();
+    bool disablePower();
+    
+    /* Enable or disable specific sensors */
+    bool enableLightSensor(bool interrupts = false);
+    bool disableLightSensor();
+    bool enableProximitySensor(bool interrupts = false);
+    bool disableProximitySensor();
+    bool enableGestureSensor(bool interrupts = true);
+    bool disableGestureSensor();
+    
+    /* LED drive strength control */
+    uint8_t getLEDDrive();
+    bool setLEDDrive(uint8_t drive);
+    uint8_t getGestureLEDDrive();
+    bool setGestureLEDDrive(uint8_t drive);
+    
+    /* Gain control */
+    uint8_t getAmbientLightGain();
+    bool setAmbientLightGain(uint8_t gain);
+    uint8_t getProximityGain();
+    bool setProximityGain(uint8_t gain);
+    uint8_t getGestureGain();
+    bool setGestureGain(uint8_t gain);
+    
+    /* Get and set light interrupt thresholds */
+    bool getLightIntLowThreshold(uint16_t &threshold);
+    bool setLightIntLowThreshold(uint16_t threshold);
+    bool getLightIntHighThreshold(uint16_t &threshold);
+    bool setLightIntHighThreshold(uint16_t threshold);
+    
+    /* Get and set proximity interrupt thresholds */
+    bool getProximityIntLowThreshold(uint8_t &threshold);
+    bool setProximityIntLowThreshold(uint8_t threshold);
+    bool getProximityIntHighThreshold(uint8_t &threshold);
+    bool setProximityIntHighThreshold(uint8_t threshold);
+    
+    /* Get and set interrupt enables */
+    uint8_t getAmbientLightIntEnable();
+    bool setAmbientLightIntEnable(uint8_t enable);
+    uint8_t getProximityIntEnable();
+    bool setProximityIntEnable(uint8_t enable);
+    uint8_t getGestureIntEnable();
+    bool setGestureIntEnable(uint8_t enable);
+    
+    /* Clear interrupts */
+    bool clearAmbientLightInt();
+    bool clearProximityInt();
+    
+    /* Ambient light methods */
+    bool readAmbientLight(uint16_t &val);
+    bool readRedLight(uint16_t &val);
+    bool readGreenLight(uint16_t &val);
+    bool readBlueLight(uint16_t &val);
+    
+    /* Proximity methods */
+    bool readProximity(uint8_t &val);
+    
+    /* Gesture methods */
+    bool isGestureAvailable();
+    int readGesture();
+    
+private:
+
+    /* Gesture processing */
+    void resetGestureParameters();
+    bool processGestureData();
+    bool decodeGesture();
+
+    /* Proximity Interrupt Threshold */
+    uint8_t getProxIntLowThresh();
+    bool setProxIntLowThresh(uint8_t threshold);
+    uint8_t getProxIntHighThresh();
+    bool setProxIntHighThresh(uint8_t threshold);
+    
+    /* LED Boost Control */
+    uint8_t getLEDBoost();
+    bool setLEDBoost(uint8_t boost);
+    
+    /* Proximity photodiode select */
+    uint8_t getProxGainCompEnable();
+    bool setProxGainCompEnable(uint8_t enable);
+    uint8_t getProxPhotoMask();
+    bool setProxPhotoMask(uint8_t mask);
+    
+    /* Gesture threshold control */
+    uint8_t getGestureEnterThresh();
+    bool setGestureEnterThresh(uint8_t threshold);
+    uint8_t getGestureExitThresh();
+    bool setGestureExitThresh(uint8_t threshold);
+    
+    /* Gesture LED, gain, and time control */
+    uint8_t getGestureWaitTime();
+    bool setGestureWaitTime(uint8_t time);
+    
+    /* Gesture mode */
+    uint8_t getGestureMode();
+    bool setGestureMode(uint8_t mode);
+
+    /* Raw I2C Commands */
+    bool wireWriteByte(uint8_t val);
+    bool wireWriteDataByte(uint8_t reg, uint8_t val);
+    bool wireWriteDataBlock(uint8_t reg, uint8_t *val, unsigned int len);
+    bool wireReadDataByte(uint8_t reg, uint8_t &val);
+    int wireReadDataBlock(uint8_t reg, uint8_t *val, unsigned int len);
+
+    /* Members */
+    gesture_data_type gesture_data_;
+    int gesture_ud_delta_;
+    int gesture_lr_delta_;
+    int gesture_ud_count_;
+    int gesture_lr_count_;
+    int gesture_near_count_;
+    int gesture_far_count_;
+    int gesture_state_;
+    int gesture_motion_;
+};
+
+#endif

+ 479 - 0
neo-sphere/neo-sphere.ino

@@ -0,0 +1,479 @@
+#include <NeoPixelBus.h>
+#include <NeoPixelAnimator.h>
+
+#define PIN_LEDS 19 //灯球Din接核心板的19引脚
+#define NUMPIXELS 194
+#define PIN_CHRG 14 //将tp4054的CHRG引脚接到esp32的14引脚
+
+#define LIGHTNESS 0.05f
+#define BRIGHTNESS 60
+
+RgbColor BLACK(0, 0, 0);
+
+RgbColor RED( 0,BRIGHTNESS, 0);
+RgbColor ORANGE( BRIGHTNESS/5,BRIGHTNESS, 0);
+RgbColor YELLOW(BRIGHTNESS/2, BRIGHTNESS/2, 0);
+RgbColor GREEN( BRIGHTNESS,0, 0);
+RgbColor BLUENESS(BRIGHTNESS/2,0, BRIGHTNESS);
+RgbColor BLUE(0, 0, BRIGHTNESS);
+RgbColor PURPLE( 0,BRIGHTNESS/2, BRIGHTNESS);
+
+RgbColor WHITE(BRIGHTNESS/5, BRIGHTNESS/5, BRIGHTNESS/5);
+
+NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> leds(NUMPIXELS, PIN_LEDS);
+
+NeoPixelAnimator animations(10); // NeoPixel animation management object
+
+#define NUM_RINGS 11
+#define RING_MAX_PIXELS 26
+#define RING_MIN_PIXELS 8
+// all rings starts at 0 ray
+byte RINGS[NUM_RINGS][RING_MAX_PIXELS] = {
+                                            {194,193,192,191,190,189,188,187},                                      // 8
+                                {186,185,184,183,182,181,180,179,178,177,176,175,174,173},                          // 14
+                        {172,171,170,169,168,167,166,165,164,163,162,161,160,159,158,157,156,155},                  // 18
+                    {154,153,152,151,150,149,148,147,146,145,144,143,142,141,140,139,138,137,136,135},              // 20
+            {134,133,132,131,130,129,128,127,126,125,124,123,122,121,120,119,118,117,116,115,114,113,112,111},      // 24
+        {110,109,108,107,106,105,104,103,102,101,100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85},  // 26
+            { 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84},      // 24
+                    { 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60},              // 20
+                        { 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40},                  // 18
+                                {  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22},                          // 14
+                                            {  1,  2,  3,  4,  5,  6,  7,  8}                                       // 8
+};
+
+byte RING_SIZES[NUM_RINGS] = {8, 14, 18, 20, 24, 26, 24, 20, 18, 14, 8};
+
+unsigned long startMillis = 0;
+short animation = 0;
+void setup() {
+  pinMode(PIN_CHRG,INPUT_PULLUP);//设置模式为输入,上拉
+  Serial.begin(115200);
+  initRandom();
+
+  leds.Begin();
+  leds.ClearTo(BLACK);
+  leds.Show();
+  //animations.StartAnimation(0, 6000, rainbowAnimation);
+ //animations.StartAnimation(0, 2000, raysRainbow);
+  //animations.StartAnimation(0, 2000, ringsRainbow);
+  //animations.StartAnimation(0, 500, xmasOrbAnimation);
+  //animations.StartAnimation(0, 1000, ringAnimation);
+  //animations.StartAnimation(0, 500, rayAnimation);
+  //animations.StartAnimation(0, 100, randomAnimation);
+  //animations.StartAnimation(0, 1500, ringAnimation2);
+}
+
+void loop() {
+
+  animations.UpdateAnimations();
+  leds.Show();
+  delay(10);
+  
+  if(digitalRead(PIN_CHRG) == LOW){//检测CHRG是否被拉低
+      leds.ClearTo(RED);
+      leds.Show();
+      delay(1000);
+      leds.ClearTo(BLACK);
+      leds.Show();
+      while(true);//开始充电模式 不继续运行后面程序了
+  }  
+  if (true && (startMillis == 0 || startMillis + 11400 < millis())) {
+    startMillis = millis();
+    switch (animation) {
+      case 0: 
+        animations.StartAnimation(0, 500, oneByOne);
+      break;
+      /*
+      case 1: 
+        animations.StartAnimation(0, 600, rayAnimation);
+        break;
+      case 2: 
+        animations.StartAnimation(0, 1200, ringAnimation);
+        break;
+       
+      case 1: 
+        animations.StartAnimation(0, 1200, ringAnimation2);
+        break; */
+      case 1:
+        animations.StartAnimation(0, 600, rayAnimation2);
+        break;      
+      case 2: 
+        animations.StartAnimation(0, 3000, raysRainbow);
+        break;
+      case 3: 
+        animations.StartAnimation(0, 2000, ringsRainbow);
+        break;
+      case 4: 
+        animations.StartAnimation(0, 8000, rainbowAnimation);
+        break;
+      case 5:
+        animations.StartAnimation(0, 100, randomAnimation);
+        animation = -1;
+        break;
+    }
+    animation ++;
+    //leds.ClearTo(BLACK);
+  }
+  
+}
+
+void randomAnimation(const AnimationParam& param) {
+  float hue;
+  HslColor color;
+
+  if (param.state == AnimationState_Completed) {
+    for (byte i = 0; i < 194; i ++) {
+      hue = random(0, 1000) / 1000.0f;
+      color = HslColor(hue, 1.0f, LIGHTNESS);
+      leds.SetPixelColor(i, color);
+    }
+
+    animations.RestartAnimation(0);
+  }
+}
+
+void rainbowAnimation(const AnimationParam& param) {
+  HslColor color = HslColor(param.progress, 1.0f, LIGHTNESS);
+  leds.ClearTo(color);
+
+  if (param.state == AnimationState_Completed) {
+    animations.RestartAnimation(0);
+  }
+}
+
+void raysRainbow(const AnimationParam& param) {
+  HslColor color;
+  float hue;
+
+  for (int i = 0; i < RING_MAX_PIXELS; i++) {
+    hue = param.progress + (float) i / (float) RING_MAX_PIXELS;
+    if (hue > 1.0f) {
+      hue -= 1.0f;
+    }
+
+    color = HslColor(hue, 1.0f, LIGHTNESS);
+    rayColor(i, RgbColor(color));
+  }
+
+  if (param.state == AnimationState_Completed) {
+    animations.RestartAnimation(0);
+  }
+}
+
+void ringsRainbow(const AnimationParam& param) {
+  HslColor color;
+  float hue;
+
+  for (int i = 0; i < NUM_RINGS; i++) {
+    hue = param.progress + (float) i / (float) NUM_RINGS;
+    if (hue > 1.0f) {
+      hue -= 1.0f;
+    }
+
+    color = HslColor(hue, 1.0f, LIGHTNESS);
+    ringColor(i, RgbColor(color));
+  }
+
+  if (param.state == AnimationState_Completed) {
+    animations.RestartAnimation(0);
+  }
+}
+void oneByOne(const AnimationParam& param) {
+  static int static_cnt = 0;
+  static int static_colorCnt = 0;
+  int cnt = param.progress * 100;
+  int index = 0;
+  RgbColor color;
+  leds.ClearTo(BLACK);
+  if(static_cnt > NUMPIXELS){
+      static_cnt = 0;
+      static_colorCnt += 1;
+      if(static_colorCnt>6){
+          static_colorCnt = 0;
+        }
+      //leds.ClearTo(BLACK);
+    }
+    switch(static_colorCnt){
+      case 0:color = RED;break;
+      case 1:color = BLUE;break;
+      case 2:color = GREEN;break;
+      case 3:color = YELLOW;break;
+      case 4:color = BLUENESS;break;
+      case 5:color = ORANGE;break;
+      case 6:color = PURPLE;break;
+      default:color = RED;break;
+      }
+      for(int i=0;i<6;i++){
+        index = static_cnt + i;
+        if(index>NUMPIXELS){
+            break;
+          }
+        if(index<85){
+            leds.SetPixelColor(index, color);   
+            }
+          else if(index<=110){
+            leds.SetPixelColor(110 - index + 84, color);
+            }
+          else if(index<=134){
+            leds.SetPixelColor(134 - index + 110, color);
+            }
+          else if(index<=154){
+            leds.SetPixelColor(154 - index + 134, color);
+            }
+          else if(index<=172){
+            leds.SetPixelColor(172 - index + 154, color);
+            }
+          else if(index<=186){
+            leds.SetPixelColor(186 - index + 172, color);
+            }
+          else{
+            leds.SetPixelColor(194 - index + 186, color);
+            }        
+        }
+    
+    //static_cnt += 1;
+    
+    if(cnt%3 == 0){
+        static_cnt += 1;      
+      }
+  if (param.state == AnimationState_Completed) {
+    animations.RestartAnimation(0);
+  }
+}
+void xmasOrbAnimation(const AnimationParam& param) {
+  ringColor(0, WHITE);
+  ringColor(1, RED);
+  ringColor(2, RED);
+  ringColor(3, RED);
+  ringColor(4, RED);
+  ringColor(5, WHITE);
+  ringColor(6, RED);
+  ringColor(7, RED);
+  ringColor(8, RED);
+  ringColor(9, RED);
+  ringColor(10, WHITE);
+
+  byte offset = round(param.progress);
+  for (byte i = offset; i < RING_SIZES[3]; i+=2) {
+    leds.SetPixelColor(RINGS[3][i] - 1, WHITE);
+  }
+  for (byte i = offset; i < RING_SIZES[7]; i+=2) {
+    leds.SetPixelColor(RINGS[7][i] - 1, WHITE);
+  }
+
+  if (param.state == AnimationState_Completed) {
+    animations.RestartAnimation(0);
+  }
+}
+void ringAnimation(const AnimationParam& param) {
+  int index = param.progress * (NUM_RINGS * 2 - 2);
+  static int static_colorCnt = 0;
+  RgbColor color;
+  if(index>=20){
+    static_colorCnt += 1;
+    if(static_colorCnt>6){
+        static_colorCnt = 0;
+      }
+    }
+  switch(static_colorCnt){
+    case 0:color = RED;break;
+    case 1:color = ORANGE;break;
+    case 2:color = YELLOW;break;
+    case 3:color = GREEN;break;
+    case 4:color = BLUENESS;break;
+    case 5:color = BLUE;break;
+    case 6:color = PURPLE;break;
+    default:color = RED;break;
+    }
+  leds.ClearTo(BLACK);
+  if (index < NUM_RINGS) {
+    ringColor(index, color);
+  }
+  else {
+    ringColor(NUM_RINGS - (index - NUM_RINGS) - 2, color);
+  }
+
+  if (param.state == AnimationState_Completed) {
+    animations.RestartAnimation(0);
+  }
+}
+void ringAnimation2(const AnimationParam& param) {
+  static int static_cnt = 0;
+  int index = param.progress * (NUM_RINGS * 2 - 2);
+  if (index>=20) {
+      static_cnt += 1;
+      if(static_cnt > 3){
+          static_cnt = 0;
+        }
+    }
+  switch(static_cnt){
+    case 0:
+      if (index < NUM_RINGS) {
+        ringColor(index, WHITE);
+      }
+      else {
+        ringColor(NUM_RINGS - (index - NUM_RINGS-1) - 2, RED);
+      }
+    break;   
+    case 1:
+      if (index < NUM_RINGS) {
+        ringColor(index, ORANGE);
+      }
+      else {
+        ringColor(NUM_RINGS - (index - NUM_RINGS-1) - 2, YELLOW);
+      }
+    break;    
+    case 2:
+      if (index < NUM_RINGS) {
+        ringColor(index, GREEN);
+      }
+      else {
+        ringColor(NUM_RINGS - (index - NUM_RINGS-1) - 2, BLUENESS);
+      }
+    break;    
+    case 3:
+      if (index < NUM_RINGS) {
+        ringColor(index, BLUE);
+      }
+      else {
+        ringColor(NUM_RINGS - (index - NUM_RINGS-1) - 2, PURPLE);
+      }
+    break;
+    }
+
+  if (param.state == AnimationState_Completed) {
+    animations.RestartAnimation(0);
+  }
+}
+
+void rayAnimation(const AnimationParam& param) {
+  int index = param.progress * (RING_MAX_PIXELS / 2);
+  static int static_colorCnt = 0;
+  RgbColor color;
+  switch(static_colorCnt){
+    case 0:color = RED;break;
+    case 1:color = ORANGE;break;
+    case 2:color = YELLOW;break;
+    case 3:color = GREEN;break;
+    case 4:color = BLUENESS;break;
+    case 5:color = BLUE;break;
+    case 6:color = PURPLE;break;
+    default:color = RED;break;
+    }
+  if (index > 12) {
+    index = 12;
+    static_colorCnt += 1;
+    if(static_colorCnt>6){
+      static_colorCnt = 0;
+    }
+  }
+ 
+  leds.ClearTo(BLACK);
+  rayColor(index, color);
+  rayColor(index + (RING_MAX_PIXELS / 2), color);
+
+  if (param.state == AnimationState_Completed) {
+    animations.RestartAnimation(0);
+  }
+}
+void rayAnimation2(const AnimationParam& param) {
+  static int static_cnt = 0;
+  int index = param.progress * (RING_MAX_PIXELS+1);
+  if (index>=26) {
+      static_cnt += 1;
+      if(static_cnt > 6){
+          static_cnt = 0;
+        }
+    }
+  switch(static_cnt){
+    case 0:
+      rayColor(index, RED);
+    break;   
+    case 1:
+      rayColor(index, ORANGE);
+    break;    
+    case 2:
+      rayColor(index, YELLOW);
+    break;    
+    case 3:
+      rayColor(index, GREEN);
+    break;
+    case 4:
+      rayColor(index, BLUENESS);
+    break;
+    case 5:
+      rayColor(index, BLUE);
+    break;
+    case 6:
+      rayColor(index, PURPLE);
+    break;
+    }
+  if (param.state == AnimationState_Completed) {
+    animations.RestartAnimation(0);
+  }
+}
+
+void rayColor(byte rayIndex, RgbColor color) {
+  int pixelIndex;
+  byte pixel;
+
+  if (rayIndex >= RING_MAX_PIXELS) {
+    return; // prevents out of bounds
+  }
+
+  for (byte i = 0; i < NUM_RINGS; i ++) {
+    pixelIndex = round((float) RING_SIZES[i] / (float) RING_MAX_PIXELS * rayIndex);
+    pixel = RINGS[i][pixelIndex];
+    if (pixel == 0) {
+      continue; // skip condition
+    }
+    leds.SetPixelColor(pixel - 1, color); // index starts from 1 (0 is stop condition)
+  }
+}
+
+void ringColor(byte ringIndex, RgbColor color) {
+  byte pixel;
+
+  if (ringIndex >= NUM_RINGS) {
+    return; // prevents out of bounds
+  }
+
+  for (byte i = 0; i < RING_MAX_PIXELS; i ++) {
+    pixel = RINGS[ringIndex][i];
+    if (pixel == 0) {
+      return; // end condition
+    }
+    leds.SetPixelColor(pixel - 1, color); // index starts from 1 (0 is stop condition)
+  }
+}
+
+void initRandom() {
+  // random works best with a seed that can use 31 bits
+  // analogRead on a unconnected pin tends toward less than four bits
+  uint32_t seed = analogRead(0);
+  delay(1);
+
+  for (int shifts = 3; shifts < 31; shifts += 3) {
+    seed ^= analogRead(0) << shifts;
+    delay(1);
+  }
+
+  // Serial.println(seed);
+  randomSeed(seed);
+}
+
+RgbColor colorWheel(byte wheelPos) {
+  // Input a value 0 to 255 to get a color value.
+  // The colours are a transition r - g - b - back to r.
+  wheelPos = 255 - wheelPos;
+  if (wheelPos < 85) {
+    return RgbColor(255 - wheelPos * 3, 0, wheelPos * 3);
+  }
+  if (wheelPos < 170) {
+    wheelPos -= 85;
+    return RgbColor(0, wheelPos * 3, 255 - wheelPos * 3);
+  }
+  wheelPos -= 170;
+  return RgbColor(wheelPos * 3, 255 - wheelPos * 3, 0);
+}

+ 595 - 0
neo-sphere/neo-sphere.ino.standard.hex

@@ -0,0 +1,595 @@
+:100000000C9463000C948B000C948B000C948B006C
+:100010000C948B000C948B000C948B000C948B0034
+:100020000C948B000C948B000C948B000C948B0024
+:100030000C948B000C948B000C948B000C948B0014
+:100040000C947D090C948B000C944B090C9425099D
+:100050000C948B000C948B000C948B000C948B00F4
+:100060000C948B000C948B000000000024002700EF
+:100070002A000000000023002600290000000008DC
+:10008000000201000003040700000000000000005F
+:10009000010204081020408001020408102001021F
+:1000A0000408102000000000250028002B00040494
+:1000B0000404040404040202020202020303030310
+:1000C0000303C7092C0D11241FBECFEFD8E0DEBFFC
+:1000D000CDBF12E0A0E0B1E0E8ECF3E202C0059091
+:1000E0000D92A634B107D9F723E0A6E4B2E001C02F
+:1000F0001D92A033B207E1F710E0C2E6D0E004C0E1
+:100100002197FE010E949810C136D107C9F70E94BD
+:10011000A30A0C94D7110C9400000F931F93209105
+:10012000F8023091F902232B09F44BC061157105D7
+:1001300009F447C02091000330910103232B99F467
+:100140004FB7F8940091570210915802209159022C
+:1001500030915A024FBF0093FC021093FD0220938E
+:10016000FE023093FF02E091FA02F091FB0220912F
+:10017000F8023091F902232B89F02091000330918D
+:1001800001032115310551F042815381452B31F096
+:10019000215031093093010320930003009711F49B
+:1001A00081E090E09183808393838283758364836D
+:1001B0008091000390910103019690930103809335
+:1001C00000031F910F910895E091FA02F091FB0254
+:1001D00080819181009721F0648175810C948D005C
+:1001E000089580E480937C0080917A0080648093FD
+:1001F0007A0080917A0086FDFCCF80917800909102
+:1002000079000895E0910F03F091100340910503E8
+:10021000509106039A01220F331F240F351F2E0F12
+:100220003F1FE217F30728F47083618382833396BC
+:10023000F8CF809107038068809307030895209189
+:100240000503309106038217930798F4FC01EE0F23
+:10025000FF1F8E0F9F1FE0910F03F0911003E80F17
+:10026000F91F5083418362838091070380688093E4
+:1002700007030895AF92BF92CF92DF92EF92FF9261
+:100280000F931F93CF93DF936C017B018B01040FBE
+:10029000151FEB015E01AE18BF08C017D10759F05A
+:1002A0006991D601ED91FC910190F081E02DC6019C
+:1002B0000995892B79F7C501DF91CF911F910F9196
+:1002C000FF90EF90DF90CF90BF90AF900895FC012A
+:1002D000538D448D252F30E0842F90E0821B930BAB
+:1002E000541710F0CF96089501970895FC01918D51
+:1002F000828D981761F0A28DAE0FBF2FB11D5D9654
+:100300008C91928D9F5F9F73928F90E008958FEFF5
+:100310009FEF0895FC01918D828D981731F0828DA9
+:10032000E80FF11D858D90E008958FEF9FEF089500
+:10033000FC01918D228D892F90E0805C9F4F821B64
+:1003400091098F73992708958BE592E00E94980197
+:1003500021E0892B09F420E0822F089580E090E0CD
+:10036000892B29F00E94A40181110C9400000895AA
+:10037000FC01A48DA80FB92FB11DA35ABF4F2C911A
+:10038000848D90E001968F739927848FA689B78911
+:100390002C93A089B1898C91837080648C93938D08
+:1003A000848D981306C00288F389E02D80818F7DAB
+:1003B00080830895EF92FF920F931F93CF93DF9363
+:1003C000EC0181E0888F9B8D8C8D98131AC0E88991
+:1003D000F989808185FF15C09FB7F894EE89FF8960
+:1003E0006083E889F98980818370806480839FBFFE
+:1003F00081E090E0DF91CF911F910F91FF90EF90FE
+:100400000895F62E0B8D10E00F5F1F4F0F7311270D
+:10041000E02E8C8D8E110CC00FB607FCFACFE88948
+:10042000F989808185FFF5CFCE010E94B801F1CF17
+:10043000EB8DEC0FFD2FF11DE35AFF4FF0829FB7BC
+:10044000F8940B8FEA89FB8980818062CFCFCF93AC
+:10045000DF93EC01888D8823B9F0AA89BB89E889EC
+:10046000F9898C9185FD03C0808186FD0DC00FB692
+:1004700007FCF7CF8C9185FFF2CF808185FFEDCF10
+:10048000CE010E94B801E9CFDF91CF91089583306A
+:1004900081F028F4813099F08230A9F008958730F6
+:1004A000A9F08830C9F08430B1F4809180008F7D4C
+:1004B00003C0809180008F7780938000089584B579
+:1004C0008F7784BD089584B58F7DFBCF8091B00078
+:1004D0008F778093B00008958091B0008F7DF9CF21
+:1004E000CF93DF9390E0FC01E057FF4F24918255BA
+:1004F0009F4FFC0184918823C9F090E0880F991FD9
+:10050000FC01E859FF4FA591B491FC01EC55FF4F58
+:10051000C591D49161110DC09FB7F8948C9120952D
+:1005200082238C938881282328839FBFDF91CF91DA
+:100530000895623051F49FB7F8943C91822F8095D2
+:1005400083238C93E8812E2BEFCF8FB7F894EC9117
+:100550002E2B2C938FBFEACF3FB7F89480914D029A
+:1005600090914E02A0914F02B091500226B5A89BE7
+:1005700005C02F3F19F00196A11DB11D3FBFBA2F35
+:10058000A92F982F8827BC01CD01620F711D811DF5
+:10059000911D42E0660F771F881F991F4A95D1F77A
+:1005A00008950F931F93CF93DF9300D000D000D016
+:1005B0001F92CDB7DEB78091070387FF66C00E9408
+:1005C000AC0200910B0310910C0320910D033091AC
+:1005D0000E03601B710B820B930B6C327140810513
+:1005E000910568F3F89480911303E0911103F09161
+:1005F0001203409108035091090320910F03309199
+:1006000010035A83498389010F5F1F4FD9019C91C1
+:100610009F839081982B9E839081809589238D83E1
+:100620008D818C8388E08B835E816D814F813B81DE
+:100630002C8189819A81D801508347FD252F3A95D5
+:100640002083262F39F0441F00C000006083000083
+:1006500000C0F2CF38E04D9160830000019761F750
+:100660004F833B832C839A83898378940E94AC02C6
+:1006700060930B0370930C0380930D0390930E0310
+:10068000809107038F778093070327960FB6F8941E
+:10069000DEBF0FBECDBFDF91CF911F910F910895A7
+:1006A0008F929F92AF92BF92CF92DF92EF92FF9282
+:1006B0004B015C010E94AC026B017C010E94AC0208
+:1006C0006C197D098E099F09683E7340810591056B
+:1006D000A8F321E0821A9108A108B10888EEC80E9B
+:1006E00083E0D81EE11CF11C81149104A104B10423
+:1006F00029F7FF90EF90DF90CF90BF90AF909F9041
+:100700008F9008952F923F924F925F926F927F92B7
+:100710008F929F92AF92BF92CF92DF92EF92FF9211
+:100720000F931F93CF93DF933B014C015901D42FBB
+:10073000C52F6701780120E030E0A901C801B601AA
+:100740000E94B30E87FF0AC020E030E040E85FE37C
+:10075000C801B6010E94470E6B017C0120E030E029
+:1007600040E85FE3C701B6010E941210181654F466
+:1007700020E030E040E85FE3C701B6010E94460E8A
+:100780006B017C012BEA3AEA4AE25EE3C701B6015B
+:100790000E94B30E87FF2EC09301A401B5018D2FD7
+:1007A0009C2F0E94460E20E030E040EC50E40E9476
+:1007B0001710A70196010E9417109301A4010E942F
+:1007C000470E5B01D82FC92FB5018D2F9C2FDF91CC
+:1007D000CF911F910F91FF90EF90DF90CF90BF903E
+:1007E000AF909F908F907F906F905F904F903F90D1
+:1007F0002F90089520E030E040E05FE3C701B601AC
+:100800000E94B30E87FDE0CF2BEA3AEA4AE25FE3AB
+:10081000C701B6010E94B30E87FF1CC09301A4015B
+:10082000B5018D2F9C2F0E94460E1B012C01A701A4
+:1008300096016BEA7AEA8AE29FE30E94460E9B01E8
+:10084000AC01C201B1010E94171020E030E040EC81
+:1008500050E4B1CF5301D82DC92DB6CF2F923F927E
+:100860004F925F926F927F928F929F92AF92BF92C0
+:10087000CF92DF92EF92FF920F931F93CF93DF936C
+:10088000CDB7DEB72A970FB6F894DEBF0FBECDBF47
+:100890001C01FB0180819181A281B38189839A83AC
+:1008A000AB83BC8384809580A680B78090859A872F
+:1008B000A185A987D284C38420E030E0A901C501C5
+:1008C000B4010E94B30E882309F4C9C020E030E0CF
+:1008D000A9016A8579858D2D9C2D0E94B30E8823F0
+:1008E00009F4BDC020E030E040E05FE36A8579852F
+:1008F0008D2D9C2D0E94B30E87FF98C020E030E024
+:1009000040E85FE3C501B4010E94470E2A8539859E
+:100910004D2D5C2D0E9417104B015C012A853985F5
+:100920004D2D5C2D6A8579858D2D9C2D0E94470E5D
+:10093000A50194010E94460E2B013C012BEA3AEAE4
+:100940004AEA5EE369817A818B819C810E94470E2D
+:100950007B018C01A5019401C301B2010E948203B5
+:100960006A877987D82EC92EE980FA800B811C818D
+:10097000A5019401C301B2010E9482036D837E83AD
+:100980008F8398872BEA3AEA4AEA5EE3C801B70107
+:100990000E94460E7B018C01A5019401C301B201A6
+:1009A0000E9482034B015C0120E030E04FE753E4FA
+:1009B0006A8579858D2D9C2D0E9417100E94310F1C
+:1009C000F101608320E030E04FE753E46D817E81E8
+:1009D0008F8198850E9417100E94310FD1011196C6
+:1009E0006C9320E030E04FE753E4B401C5010E946E
+:1009F00017100E94310FF10162832A960FB6F89406
+:100A0000DEBF0FBECDBFDF91CF911F910F91FF9041
+:100A1000EF90DF90CF90BF90AF909F908F907F909E
+:100A20006F905F904F903F902F9008952A85398561
+:100A30004D2D5C2DC501B4010E94470E2B013C01D8
+:100A40002A8539854D2D5C2DC501B4010E941710F2
+:100A50009B01AC01C301B2010E94460E5DCF8A84A6
+:100A60009984AD2CBC2C8D829E82DF82C8869CCF5F
+:100A70002F923F924F925F926F927F928F92AF929E
+:100A8000BF92CF92DF92EF92FF920F931F93CF937B
+:100A9000DF937C0120E030E048EC52E4FC0160810F
+:100AA0007181828193810E9417100E942A0F5B013D
+:100AB0006091290370912A0380912B030E94020107
+:100AC0008091480290914902833C910584F0809185
+:100AD00046029091470210924902109248020196F4
+:100AE0008730910554F59093470280934602809198
+:100AF00046029091470260902003709021038090FD
+:100B0000220383309105C1F1ECF4609017037090DB
+:100B10001803809019038130910571F160901D03D5
+:100B200070901E0380901F03029731F1609026039E
+:100B300070902703809028031FC0109247021092E4
+:100B40004602D5CF60902303709024038090250344
+:100B50008530910589F060901A0370901B03809096
+:100B60001C038530910544F06090140370901503C8
+:100B7000809016030697D1F6D0E0C0E08CE7482EAF
+:100B80005524539496E6292E3324339406E411E039
+:100B9000C0904802D0904902C6018C0F9D1F833C33
+:100BA00091055CF4682DA301853591058CF50E94B3
+:100BB0001F012196C630D10559F7C50163E070E0E9
+:100BC0000E948410892B31F4C6010196909349024A
+:100BD00080934802F701868197810297C9F5809139
+:100BE000F8029091F902892B99F1DF91CF911F9131
+:100BF0000F91FF90EF90DF90CF90BF90AF908F90CC
+:100C00007F906F905F904F903F902F900C94E400F6
+:100C100022EC30E08F36910594F0E4EFF0E0873875
+:100C2000910594F020E231E08B39910544F0F80110
+:100C30008D3A91054CF08B3B91052CF49101281BCA
+:100C4000390BC901B4CFF201E81BF90BCF01AFCFCB
+:100C5000DF91CF911F910F91FF90EF90DF90CF9098
+:100C6000BF90AF908F907F906F905F904F903F902C
+:100C70002F9008958091F8029091F902892B11F03C
+:100C80000C94E40008950F931F93CF93DF93CDB797
+:100C9000DEB72F970FB6F894DEBF0FBECDBF8C0125
+:100CA000FC0180819181A281B38189839A83AB8386
+:100CB000BC8380E090E0A0E8BFE38D839E83AF8398
+:100CC000B8878DEC9CECACE4BDE389879A87AB8751
+:100CD000BC87BE016F5F7F4FCE010D960E942E0430
+:100CE0006D857E858F850E940201F801868197813E
+:100CF000029711F40E943A062F960FB6F894DEBFC1
+:100D00000FBECDBFDF91CF911F910F9108952F920C
+:100D10003F924F925F926F927F928F92BF92CF924B
+:100D2000DF92EF92FF920F931F93CF93DF938A315D
+:100D300008F051C03A01862E08E212E0D0E0C0E08F
+:100D4000C82ED12CF12CE12C8AE1B82EF80161914A
+:100D50008F0170E090E080E00E94600F20E030E0C2
+:100D600040ED51E40E94B80E1B012C01C701B601F1
+:100D70000E94620F9B01AC01C201B1010E941710D9
+:100D80001B012C0120E030E0A9010E94121020E09C
+:100D900030E040E05FE387FD2FC0C201B1010E9457
+:100DA000470E0E942A0FBC9EC001BD9E900D1124CB
+:100DB000865F9E4F680F791FFB018081882331F089
+:100DC000682DA3018150990B0E941F012196CB3001
+:100DD000D10509F0BBCFDF91CF911F910F91FF900B
+:100DE000EF90DF90CF90BF908F907F906F905F904B
+:100DF0004F903F902F900895C201B1010E94460E7E
+:100E0000D0CF4F925F926F927F92AF92BF92CF926C
+:100E1000DF92EF92FF920F931F93CF93DF93CDB7A3
+:100E2000DEB72F970FB6F894DEBF0FBECDBF5C01C3
+:100E300010E000E08DEC482E8CEC582E8CE4682EEF
+:100E40008DE3782EB801012E000C880B990B0E94BF
+:100E5000620F20E030E040ED51E40E94B80EF50151
+:100E600020813181428153810E94470E6B017C01B8
+:100E700020E030E040E85FE30E941210181654F4BE
+:100E800020E030E040E85FE3C701B6010E94460E73
+:100E90006B017C01C982DA82EB82FC8280E090E007
+:100EA000A0E8BFE38D839E83AF83B88749865A86C7
+:100EB0006B867C86BE016F5F7F4FCE010D960E94D0
+:100EC0002E044D855E856F85802F0E9487060F5FFB
+:100ED0001F4F0A31110509F0B5CFF50186819781C1
+:100EE000029711F40E943A062F960FB6F894DEBFCF
+:100EF0000FBECDBFDF91CF911F910F91FF90EF906B
+:100F0000DF90CF90BF90AF907F906F905F904F90A9
+:100F10000895CF93DF93EC0120E030E048ED51E4F9
+:100F2000688179818A819B810E9417100E942A0F13
+:100F3000CB016A3171056CF020914A0230914B026D
+:100F40002F5F3F4F27303105B4F430934B0220938D
+:100F50004A02E0914A02F0914B02E730F105C0F4F9
+:100F6000EC54F84F0C949810C007D607DD07E4073F
+:100F7000EB07F207F90710924B0210924A02E9CFF1
+:100F80004091260350912703609128030E94870611
+:100F90008E819F810297A1F58091F8029091F902CC
+:100FA000892B71F1DF91CF910C94E40040912303E0
+:100FB0005091240360912503E9CF40912003509183
+:100FC000210360912203E2CF40911D0350911E0343
+:100FD00060911F03DBCF40911A0350911B03609176
+:100FE0001C03D4CF4091170350911803609119034B
+:100FF000CDCF409114035091150360911603C6CFD5
+:10100000DF91CF9108952F923F924F925F926F920E
+:101010007F928F929F92AF92BF92CF92DF92EF9288
+:10102000FF920F931F93CF93DF93CDB7DEB72F9728
+:101030000FB6F894DEBF0FBECDBF5C018AE0C82EAC
+:1010400081E0D82E10E000E09DEC292E9CEC392E9A
+:101050009CE4492E9DE3592EB801012E000C880B0B
+:10106000990B0E94620F20E030E040E351E40E94BF
+:10107000B80EF50120813181428153810E94470ED3
+:101080003B014C0120E030E040E85FE30E94121099
+:10109000181654F420E030E040E85FE3C401B301E7
+:1010A0000E94460E3B014C0169827A828B829C82AF
+:1010B00080E090E0A0E8BFE38D839E83AF83B88794
+:1010C00029863A864B865C86BE016F5F7F4FCE01D4
+:1010D0000D960E942E046D847E848F84F12CE12C69
+:1010E000F601EE0DFF1D8081882369F0682DA301B4
+:1010F0008150990B0E941F01FFEFEF1AFF0A8AE14E
+:10110000E816F10469F70F5F1F4FEAE1CE0ED11C1C
+:101110000B30110509F0A0CFF50186819781029768
+:1011200011F40E943A062F960FB6F894DEBF0FBE58
+:10113000CDBFDF91CF911F910F91FF90EF90DF9086
+:10114000CF90BF90AF909F908F907F906F905F9067
+:101150004F903F902F9008954F925F926F927F92A1
+:101160008F929F92AF92BF92CF92DF92EF92FF92B7
+:101170000F931F93CF93DF93CDB7DEB72F970FB6A3
+:10118000F894DEBF0FBECDBFFC0186819781029728
+:1011900009F044C010E000E088EE482E83E0582EAD
+:1011A000612C712C812C912C90E8A92E9FE3B92EF3
+:1011B0002DECC22E2CECD22E2CE4E22E2DE3F22EBE
+:1011C0000E948B0DA30192010E94E30D0E94620F09
+:1011D00020E030E04AE754E40E94B80E69837A8345
+:1011E0008B839C838D829E82AF82B886C986DA8685
+:1011F000EB86FC86BE016F5F7F4FCE010D960E948D
+:101200002E044D855E856F85C8010E941F010F5F0A
+:101210001F4F023C1105A1F60E943A062F960FB609
+:10122000F894DEBF0FBECDBFDF91CF911F910F911C
+:10123000FF90EF90DF90CF90BF90AF909F908F90F6
+:101240007F906F905F904F9008951F920F920FB60E
+:101250000F9211242F933F934F935F936F937F933C
+:101260008F939F93AF93BF93EF93FF938BE592E0A0
+:101270000E94B801FF91EF91BF91AF919F918F9123
+:101280007F916F915F914F913F912F910F900FBE82
+:101290000F901F9018951F920F920FB60F92112466
+:1012A0002F938F939F93EF93FF93E0916B02F091B5
+:1012B0006C028081E0917102F091720282FD1BC08C
+:1012C0009081809174028F5F8F73209175028217D5
+:1012D00041F0E0917402F0E0E55AFD4F958F809364
+:1012E0007402FF91EF919F918F912F910F900FBEFC
+:1012F0000F901F9018958081F4CF1F920F920FB618
+:101300000F9211242F933F938F939F93AF93BF938B
+:101310008091570290915802A0915902B0915A02BF
+:1013200030914C0223E0230F2D3758F50196A11D73
+:10133000B11D20934C028093570290935802A093C2
+:101340005902B0935A0280914D0290914E02A091A1
+:101350004F02B09150020196A11DB11D80934D0224
+:1013600090934E02A0934F02B0935002BF91AF9161
+:101370009F918F913F912F910F900FBE0F901F90D3
+:10138000189526E8230F0296A11DB11DD2CF109209
+:101390005E0210925D0288EE93E0A0E0B0E08093E0
+:1013A0005F0290936002A0936102B093620287E3B0
+:1013B00092E090935C0280935B0285EC90E09093C6
+:1013C00068028093670284EC90E090936A028093B5
+:1013D000690280EC90E090936C0280936B0281EC48
+:1013E00090E090936E0280936D0222EC30E0309397
+:1013F000700220936F0286EC90E0909372028093CB
+:10140000710210927402109275021092760210927C
+:1014100077021092290310922A0310922B03109244
+:1014200026038CE380932703109228039CE090937B
+:10143000230380932403109225039EE190932003BD
+:10144000909321031092220380931D0310921E0398
+:1014500010921F0390931A0310921B0380931C0396
+:10146000109217031092180380931903109214031B
+:10147000909315038093160330930603209305037E
+:101480001092070386E492E0909309038093080387
+:1014900083E180930A0310921203109211031092B9
+:1014A000130361E00E947002809108039091090388
+:1014B0000E94B2109093100380930F03E1ECF0E0D0
+:1014C000E491F0E0EE0FFF1FEC55FF4F85919491F2
+:1014D0009093120380931103E3EAF0E0E491E09328
+:1014E00013038AE090E09093F9028093F80210923F
+:1014F000FC021092FD021092FE021092FF02109266
+:1015000001031092000381E08093040381E090E0E6
+:1015100090930303809302038CE390E00E94B21047
+:10152000FC019C01245C3F4F1182108213821282C5
+:10153000158214823696E217F307B1F79093FB02F7
+:101540008093FA020895CF93DF93CDB7DEB72E973D
+:101550000FB6F894DEBF0FBECDBF789484B582601D
+:1015600084BD84B5816084BD85B5826085BD85B547
+:10157000816085BD80916E00816080936E001092C5
+:101580008100809181008260809381008091810040
+:1015900081608093810080918000816080938000D1
+:1015A0008091B10084608093B1008091B00081602F
+:1015B0008093B00080917A00846080937A0080915B
+:1015C0007A00826080937A0080917A0081608093B3
+:1015D0007A0080917A00806880937A001092C1002E
+:1015E00062E08EE00E947002E0916B02F0916C026A
+:1015F00082E08083E0916702F09168021082E091BE
+:101600006902F0916A0280E1808310927302E09196
+:101610006F02F091700286E08083E0916D02F0919C
+:101620006E02808180618083E0916D02F0916E0294
+:10163000808188608083E0916D02F0916E028081EC
+:1016400080688083E0916D02F0916E0280818F7DD1
+:1016500080830E94F1006C01990FEE08FF0861E0A1
+:1016600070E080E090E00E94500303E010E00E94F0
+:10167000F100002E02C0880F991F0A94E2F74C0176
+:10168000990FAA08BB08240135014C245D246E245F
+:101690007F2461E070E080E090E00E9450030D5FE5
+:1016A0001F4F0132110509F0E1C08C149D04AE04F6
+:1016B000BF0421F0C301B2010E94DA0D20910A0398
+:1016C00030E0F901E458FF4F8491F901E057FF4FF2
+:1016D0000491F901E255FF4F1491112391F081110A
+:1016E0000E944702E12FF0E0EE0FFF1FEC55FF4F85
+:1016F000A591B4918FB7F894EC9100950E230C93BB
+:101700008FBF0E94AC0260930B0370930C03809315
+:101710000D0390930E0360E070E080E00E940201F0
+:101720006091290370912A0380912B030E9402018A
+:101730000E94D10280910403882309F4C4C08FB7AA
+:10174000F894C0905702D0905802E0905902F0905F
+:101750005A028FBF8091FC029091FD02A091FE027F
+:10176000B091FF02A7019601281B390B4A0B5B0BB6
+:10177000CA01B901209102033091030350E040E017
+:10178000621773078407950708F49DC00E94150E21
+:1017900049015A011C861B8610E000E08091F80286
+:1017A0009091F9020817190708F085C02090FA02F5
+:1017B0003090FB024B855C85240E351ED1011496BA
+:1017C0000D90BC91A02DBE87AD871E830D83F101C6
+:1017D00082819381AC0170E060E084169506A606D4
+:1017E000B70608F046C0208131813A87298721E079
+:1017F00030E049855A858417950711F430E020E0E0
+:1018000038872F8369857A85681B790B90E080E0A3
+:101810000E94600F2B013C01A985BA85BD0190E0B3
+:1018200080E00E94600F9B01AC01C301B2010E94E5
+:10183000B80E69837A838B839C83CE010196ED85F4
+:10184000FE850995D10112968D919C911397881967
+:10185000990913969C938E9312970F5F1F4F2B85B8
+:101860003C852A5F3F4F3C872B8798CF73016201ED
+:10187000FECE892B91F3E2E0F0E0F887EF8320E0E1
+:1018800030E040E85FE329833A834B835C838091B7
+:10189000000390910103019790930103809300034B
+:1018A000D10113961C921E921297CE010196ED85DE
+:1018B000FE850995D2CFC092FC02D092FD02E09243
+:1018C000FE02F092FF020E94D1026AE070E080E026
+:1018D00090E00E945003EAE8F0E08491EEE9F0E045
+:1018E0001491ECEBF0E00491002309F45DC0811148
+:1018F0000E944702E02FF0E0EE0FFF1FEE58FF4F6F
+:10190000A591B4918C91182309F44EC08091530293
+:1019100090915402A0915502B09156020097A105F2
+:10192000B10509F05CC02FB7F894809157029091EF
+:101930005802A0915902B0915A022FBF80935302CE
+:1019400090935402A0935502B09356028091510295
+:10195000909152028230910509F46FC084F400978F
+:1019600009F45FC0019709F463C08091510290911E
+:1019700052020196909352028093510245C08430E6
+:10198000910509F464C00CF45DC0059771F76CEA29
+:1019900078E084E690E00E948D008FEF9FEF9093B7
+:1019A000520280935102E1CF609126037091270388
+:1019B000809128030E9402010E94D10268EE73E028
+:1019C00080E090E00E9450036091290370912A0307
+:1019D00080912B030E9402010E94D102FFCF2FB7FA
+:1019E000F8944091570250915802609159027091B9
+:1019F0005A022FBF8857934DAF4FBF4F841795079B
+:101A0000A607B70708F48FCF80E090E0892B09F490
+:101A100091CE0E94A401882309F48CCE0E9400007C
+:101A200089CE68E375E084EF91E00E948D009DCF40
+:101A300069E877E088E592E0F8CF61E077E088EB4D
+:101A40009BE0F3CF63E078E080ED97E0EECF63E4D6
+:101A500076E080E49FE1E9CF8091FA029091FB0269
+:101A6000009711F00E944E1160E080910A030E94DD
+:101A7000700280910F03909110030C944E118F927D
+:101A80009F92AF92BF92CF92DF92EF92FF92CF934D
+:101A9000DF93EC01688179818A819B8161157105F1
+:101AA0008105910521F464E279ED8BE597E02DE164
+:101AB00033EF41E050E00E94E30D49015A019B01E0
+:101AC000AC01A7EAB1E40E94020E6B017C01ACEE0E
+:101AD000B4EFA50194010E94100EC60ED71EE81E99
+:101AE000F91EF7FE06C081E0C81AD108E10880E8B7
+:101AF000F80AC882D982EA82FB82C701B6019F77C1
+:101B0000DF91CF91FF90EF90DF90CF90BF90AF909B
+:101B10009F908F9008958F929F92AF92BF92CF9295
+:101B2000DF92EF92FF92609100017091010180912C
+:101B3000020190910301611571058105910521F460
+:101B400064E279ED8BE597E02DE133EF41E050E081
+:101B50000E94E30D49015A019B01AC01A7EAB1E4DF
+:101B60000E94020E6B017C01ACEEB4EFA501940162
+:101B70000E94100EC60ED71EE81EF91EF7FE06C004
+:101B800081E0C81AD108E10880E8F80AC092000193
+:101B9000D0920101E0920201F0920301C701B60167
+:101BA0009F77FF90EF90DF90CF90BF90AF909F9086
+:101BB0008F9008956093000170930101809302015A
+:101BC000909303010895052E97FB1EF400940E9444
+:101BD000FA0D57FD07D00E94150E07FC03D04EF4F6
+:101BE0000C94FA0D50954095309521953F4F4F4FED
+:101BF0005F4F089590958095709561957F4F8F4FB9
+:101C00009F4F08950E94370EA59F900DB49F900D91
+:101C1000A49F800D911D11240895B7FF0C94020E0E
+:101C20000E94020E821B930B0895A1E21A2EAA1B9A
+:101C3000BB1BFD010DC0AA1FBB1FEE1FFF1FA2177C
+:101C4000B307E407F50720F0A21BB30BE40BF50B79
+:101C5000661F771F881F991F1A9469F76095709502
+:101C6000809590959B01AC01BD01CF010895A29F85
+:101C7000B001B39FC001A39F700D811D1124911D60
+:101C8000B29F700D811D1124911D08955058BB27DE
+:101C9000AA270E945E0E0C94D80F0E94CA0F38F03B
+:101CA0000E94D10F20F039F49F3F19F426F40C94D0
+:101CB000C70F0EF4E095E7FB0C94C10FE92F0E94CB
+:101CC000E90F58F3BA17620773078407950720F0E6
+:101CD00079F4A6F50C940B100EF4E0950B2EBA2FA8
+:101CE000A02D0B01B90190010C01CA01A001112422
+:101CF000FF27591B99F0593F50F4503E68F11A16CE
+:101D0000F040A22F232F342F4427585FF3CF46955E
+:101D100037952795A795F0405395C9F77EF41F1680
+:101D2000BA0B620B730B840BBAF09150A1F0FF0F4A
+:101D3000BB1F661F771F881FC2F70EC0BA0F621F36
+:101D4000731F841F48F4879577956795B795F79526
+:101D50009E3F08F0B0CF9395880F08F09927EE0FBB
+:101D60009795879508950E949D0F08F481E0089546
+:101D70000E94CC0E0C94D80F0E94D10F58F00E94F4
+:101D8000CA0F40F029F45F3F29F00C94C10F5111A4
+:101D90000C940C100C94C70F0E94E90F68F3992360
+:101DA000B1F3552391F3951B550BBB27AA27621757
+:101DB0007307840738F09F5F5F4F220F331F441F64
+:101DC000AA1FA9F335D00E2E3AF0E0E832D0915098
+:101DD0005040E695001CCAF72BD0FE2F29D0660F85
+:101DE000771F881FBB1F261737074807AB07B0E8C8
+:101DF00009F0BB0B802DBF01FF2793585F4F3AF0CE
+:101E00009E3F510578F00C94C10F0C940C105F3F6D
+:101E1000E4F3983ED4F3869577956795B795F79553
+:101E20009F5FC9F7880F911D9695879597F908953B
+:101E3000E1E0660F771F881FBB1F621773078407D7
+:101E4000BA0720F0621B730B840BBA0BEE1F88F7E6
+:101E5000E09508950E94310F6894B1110C940C1014
+:101E600008950E94F10F88F09F5798F0B92F992795
+:101E7000B751B0F0E1F0660F771F881F991F1AF075
+:101E8000BA95C9F714C0B13091F00E940B10B1E0BF
+:101E900008950C940B10672F782F8827B85F39F0BE
+:101EA000B93FCCF3869577956795B395D9F73EF40E
+:101EB00090958095709561957F4F8F4F9F4F0895B6
+:101EC000E89409C097FB3EF49095809570956195D4
+:101ED0007F4F8F4F9F4F9923A9F0F92F96E9BB278A
+:101EE0009395F695879577956795B795F111F8CF06
+:101EF000FAF4BB0F11F460FF1BC06F5F7F4F8F4F71
+:101F00009F4F16C0882311F096E911C0772321F066
+:101F10009EE8872F762F05C0662371F096E8862FFE
+:101F200070E060E02AF09A95660F771F881FDAF755
+:101F3000880F9695879597F90895990F0008550F82
+:101F4000AA0BE0E8FEEF16161706E807F907C0F03F
+:101F500012161306E407F50798F0621B730B840B47
+:101F6000950B39F40A2661F0232B242B252B21F421
+:101F700008950A2609F4A140A6958FEF811D811DC1
+:101F8000089597F99F6780E870E060E008959FEFFB
+:101F900080EC089500240A94161617061806090600
+:101FA000089500240A9412161306140605060895CF
+:101FB000092E0394000C11F4882352F0BB0F40F457
+:101FC000BF2B11F460FF04C06F5F7F4F8F4F9F4F97
+:101FD000089557FD9058440F551F59F05F3F71F019
+:101FE0004795880F97FB991F61F09F3F79F0879580
+:101FF0000895121613061406551FF2CF4695F1DF09
+:1020000008C0161617061806991FF1CF8695710598
+:10201000610508940895E894BB2766277727CB01CC
+:1020200097F908950E949D0F08F48FEF08950E947C
+:102030002A100C94D80F0E94CA0F38F00E94D10FBA
+:1020400020F0952311F00C94C10F0C94C70F1124AC
+:102050000C940C100E94E90F70F3959FC1F3950F3B
+:1020600050E0551F629FF001729FBB27F00DB11D1C
+:10207000639FAA27F00DB11DAA1F649F6627B00DAC
+:10208000A11D661F829F2227B00DA11D621F739F95
+:10209000B00DA11D621F839FA00D611D221F749FA3
+:1020A0003327A00D611D231F849F600D211D822FEA
+:1020B000762F6A2F11249F5750409AF0F1F0882311
+:1020C0004AF0EE0FFF1FBB1F661F771F881F91503E
+:1020D0005040A9F79E3F510580F00C94C10F0C941D
+:1020E0000C105F3FE4F3983ED4F38695779567959F
+:1020F000B795F795E7959F5FC1F7FE2B880F911D68
+:102100009695879597F9089597FB072E16F40094F6
+:1021100007D077FD09D00E949E1007FC05D03EF441
+:10212000909581959F4F0895709561957F4F089583
+:10213000EE0FFF1F0590F491E02D0994AA1BBB1B25
+:1021400051E107C0AA1FBB1FA617B70710F0A61BB7
+:10215000B70B881F991F5A95A9F780959095BC01D8
+:10216000CD0108950F931F93CF93DF938230910594
+:1021700010F482E090E0E0912E03F0912F0330E024
+:1021800020E0B0E0A0E0309799F42115310509F482
+:102190004AC0281B390B24303105D8F58A819B8130
+:1021A0006115710589F1FB0193838283FE0111C0E2
+:1021B000408151810281138148175907E0F0481787
+:1021C000590799F4109761F012960C931297139691
+:1021D0001C933296CF01DF91CF911F910F910895FB
+:1021E00000932E0310932F03F4CF2115310551F0E6
+:1021F0004217530738F0A901DB019A01BD01DF0145
+:10220000F801C1CFEF01F9CF90932F0380932E03F4
+:10221000CDCFFE01E20FF31F81939193225031093C
+:1022200039832883D7CF20912C0330912D03232B82
+:1022300041F4209106013091070130932D03209342
+:102240002C0320910401309105012115310541F441
+:102250002DB73EB74091080150910901241B350B61
+:10226000E0912C03F0912D03E217F307A0F42E1B4D
+:102270003F0B2817390778F0AC014E5F5F4F2417EA
+:10228000350748F04E0F5F1F50932D0340932C03EA
+:10229000819391939FCFF0E0E0E09CCFCF93DF93C9
+:1022A0000097E9F0FC01329713821282A0912E036D
+:1022B000B0912F03ED0130E020E01097A1F42081D0
+:1022C0003181820F931F20912C0330912D03281709
+:1022D000390709F061C0F0932D03E0932C03DF91DF
+:1022E000CF910895EA01CE17DF07E8F54A815B81B7
+:1022F0009E0141155105B1F7E901FB83EA8349913C
+:102300005991C40FD51FEC17FD0761F480819181AD
+:102310000296840F951FE9019983888382819381B6
+:102320009B838A83F0E0E0E012968D919C91139755
+:102330000097B9F52D913C911197CD010296820F2E
+:10234000931F20912C0330912D032817390739F65C
+:10235000309751F510922F0310922E03B0932D0356
+:10236000A0932C03BCCFD383C28340815181840FBF
+:10237000951FC817D90761F44E5F5F4F8881998117
+:10238000480F591F518340838A819B8193838283A5
+:102390002115310509F0B0CFF0932F03E0932E0300
+:1023A0009ECFFD01DC01C0CF13821282D7CF10E097
+:1023B000C2E6D0E004C0FE010E9498102196C33608
+:0823C000D107C9F7F894FFCF23
+:1023C80001000000000030038000C2C1C0BFBEBDD4
+:1023D800BCBB00000000000000000000000000007E
+:1023E80000000000BAB9B8B7B6B5B4B3B2B1B0AF6F
+:1023F800AEAD000000000000000000000000ACAB23
+:10240800AAA9A8A7A6A5A4A3A2A1A09F9E9D9C9B9C
+:1024180000000000000000009A9998979695949300
+:102428009291908F8E8D8C8B8A898887000000000E
+:102438000000868584838281807F7E7D7C7B7A799B
+:102448007877767574737271706F00006E6D6C6B4F
+:102458006A696867666564636261605F5E5D5C5B4C
+:102468005A59585756553D3E3F40414243444546C8
+:102478004748494A4B4C4D4E4F5051525354000017
+:10248800292A2B2C2D2E2F3031323334353637383C
+:10249800393A3B3C0000000000001718191A1B1CB1
+:1024A8001D1E1F2021222324252627280000000086
+:1024B80000000000090A0B0C0D0E0F101112131466
+:1024C80015160000000000000000000000000102D6
+:1024D80003040506070800000000000000000000D3
+:1024E8000000000000000000080E1214181A18144A
+:1024F800120E0800000000DA013A0167012702986D
+:062508000176018A0100CA
+:00000001FF

+ 628 - 0
neo-sphere/neo-sphere.ino.with_bootloader.standard.hex

@@ -0,0 +1,628 @@
+:020000040000FA
+:100000000C9463000C948B000C948B000C948B006C
+:100010000C948B000C948B000C948B000C948B0034
+:100020000C948B000C948B000C948B000C948B0024
+:100030000C948B000C948B000C948B000C948B0014
+:100040000C947D090C948B000C944B090C9425099D
+:100050000C948B000C948B000C948B000C948B00F4
+:100060000C948B000C948B000000000024002700EF
+:100070002A000000000023002600290000000008DC
+:10008000000201000003040700000000000000005F
+:10009000010204081020408001020408102001021F
+:1000A0000408102000000000250028002B00040494
+:1000B0000404040404040202020202020303030310
+:1000C0000303C7092C0D11241FBECFEFD8E0DEBFFC
+:1000D000CDBF12E0A0E0B1E0E8ECF3E202C0059091
+:1000E0000D92A634B107D9F723E0A6E4B2E001C02F
+:1000F0001D92A033B207E1F710E0C2E6D0E004C0E1
+:100100002197FE010E949810C136D107C9F70E94BD
+:10011000A30A0C94D7110C9400000F931F93209105
+:10012000F8023091F902232B09F44BC061157105D7
+:1001300009F447C02091000330910103232B99F467
+:100140004FB7F8940091570210915802209159022C
+:1001500030915A024FBF0093FC021093FD0220938E
+:10016000FE023093FF02E091FA02F091FB0220912F
+:10017000F8023091F902232B89F02091000330918D
+:1001800001032115310551F042815381452B31F096
+:10019000215031093093010320930003009711F49B
+:1001A00081E090E09183808393838283758364836D
+:1001B0008091000390910103019690930103809335
+:1001C00000031F910F910895E091FA02F091FB0254
+:1001D00080819181009721F0648175810C948D005C
+:1001E000089580E480937C0080917A0080648093FD
+:1001F0007A0080917A0086FDFCCF80917800909102
+:1002000079000895E0910F03F091100340910503E8
+:10021000509106039A01220F331F240F351F2E0F12
+:100220003F1FE217F30728F47083618382833396BC
+:10023000F8CF809107038068809307030895209189
+:100240000503309106038217930798F4FC01EE0F23
+:10025000FF1F8E0F9F1FE0910F03F0911003E80F17
+:10026000F91F5083418362838091070380688093E4
+:1002700007030895AF92BF92CF92DF92EF92FF9261
+:100280000F931F93CF93DF936C017B018B01040FBE
+:10029000151FEB015E01AE18BF08C017D10759F05A
+:1002A0006991D601ED91FC910190F081E02DC6019C
+:1002B0000995892B79F7C501DF91CF911F910F9196
+:1002C000FF90EF90DF90CF90BF90AF900895FC012A
+:1002D000538D448D252F30E0842F90E0821B930BAB
+:1002E000541710F0CF96089501970895FC01918D51
+:1002F000828D981761F0A28DAE0FBF2FB11D5D9654
+:100300008C91928D9F5F9F73928F90E008958FEFF5
+:100310009FEF0895FC01918D828D981731F0828DA9
+:10032000E80FF11D858D90E008958FEF9FEF089500
+:10033000FC01918D228D892F90E0805C9F4F821B64
+:1003400091098F73992708958BE592E00E94980197
+:1003500021E0892B09F420E0822F089580E090E0CD
+:10036000892B29F00E94A40181110C9400000895AA
+:10037000FC01A48DA80FB92FB11DA35ABF4F2C911A
+:10038000848D90E001968F739927848FA689B78911
+:100390002C93A089B1898C91837080648C93938D08
+:1003A000848D981306C00288F389E02D80818F7DAB
+:1003B00080830895EF92FF920F931F93CF93DF9363
+:1003C000EC0181E0888F9B8D8C8D98131AC0E88991
+:1003D000F989808185FF15C09FB7F894EE89FF8960
+:1003E0006083E889F98980818370806480839FBFFE
+:1003F00081E090E0DF91CF911F910F91FF90EF90FE
+:100400000895F62E0B8D10E00F5F1F4F0F7311270D
+:10041000E02E8C8D8E110CC00FB607FCFACFE88948
+:10042000F989808185FFF5CFCE010E94B801F1CF17
+:10043000EB8DEC0FFD2FF11DE35AFF4FF0829FB7BC
+:10044000F8940B8FEA89FB8980818062CFCFCF93AC
+:10045000DF93EC01888D8823B9F0AA89BB89E889EC
+:10046000F9898C9185FD03C0808186FD0DC00FB692
+:1004700007FCF7CF8C9185FFF2CF808185FFEDCF10
+:10048000CE010E94B801E9CFDF91CF91089583306A
+:1004900081F028F4813099F08230A9F008958730F6
+:1004A000A9F08830C9F08430B1F4809180008F7D4C
+:1004B00003C0809180008F7780938000089584B579
+:1004C0008F7784BD089584B58F7DFBCF8091B00078
+:1004D0008F778093B00008958091B0008F7DF9CF21
+:1004E000CF93DF9390E0FC01E057FF4F24918255BA
+:1004F0009F4FFC0184918823C9F090E0880F991FD9
+:10050000FC01E859FF4FA591B491FC01EC55FF4F58
+:10051000C591D49161110DC09FB7F8948C9120952D
+:1005200082238C938881282328839FBFDF91CF91DA
+:100530000895623051F49FB7F8943C91822F8095D2
+:1005400083238C93E8812E2BEFCF8FB7F894EC9117
+:100550002E2B2C938FBFEACF3FB7F89480914D029A
+:1005600090914E02A0914F02B091500226B5A89BE7
+:1005700005C02F3F19F00196A11DB11D3FBFBA2F35
+:10058000A92F982F8827BC01CD01620F711D811DF5
+:10059000911D42E0660F771F881F991F4A95D1F77A
+:1005A00008950F931F93CF93DF9300D000D000D016
+:1005B0001F92CDB7DEB78091070387FF66C00E9408
+:1005C000AC0200910B0310910C0320910D033091AC
+:1005D0000E03601B710B820B930B6C327140810513
+:1005E000910568F3F89480911303E0911103F09161
+:1005F0001203409108035091090320910F03309199
+:1006000010035A83498389010F5F1F4FD9019C91C1
+:100610009F839081982B9E839081809589238D83E1
+:100620008D818C8388E08B835E816D814F813B81DE
+:100630002C8189819A81D801508347FD252F3A95D5
+:100640002083262F39F0441F00C000006083000083
+:1006500000C0F2CF38E04D9160830000019761F750
+:100660004F833B832C839A83898378940E94AC02C6
+:1006700060930B0370930C0380930D0390930E0310
+:10068000809107038F778093070327960FB6F8941E
+:10069000DEBF0FBECDBFDF91CF911F910F910895A7
+:1006A0008F929F92AF92BF92CF92DF92EF92FF9282
+:1006B0004B015C010E94AC026B017C010E94AC0208
+:1006C0006C197D098E099F09683E7340810591056B
+:1006D000A8F321E0821A9108A108B10888EEC80E9B
+:1006E00083E0D81EE11CF11C81149104A104B10423
+:1006F00029F7FF90EF90DF90CF90BF90AF909F9041
+:100700008F9008952F923F924F925F926F927F92B7
+:100710008F929F92AF92BF92CF92DF92EF92FF9211
+:100720000F931F93CF93DF933B014C015901D42FBB
+:10073000C52F6701780120E030E0A901C801B601AA
+:100740000E94B30E87FF0AC020E030E040E85FE37C
+:10075000C801B6010E94470E6B017C0120E030E029
+:1007600040E85FE3C701B6010E941210181654F466
+:1007700020E030E040E85FE3C701B6010E94460E8A
+:100780006B017C012BEA3AEA4AE25EE3C701B6015B
+:100790000E94B30E87FF2EC09301A401B5018D2FD7
+:1007A0009C2F0E94460E20E030E040EC50E40E9476
+:1007B0001710A70196010E9417109301A4010E942F
+:1007C000470E5B01D82FC92FB5018D2F9C2FDF91CC
+:1007D000CF911F910F91FF90EF90DF90CF90BF903E
+:1007E000AF909F908F907F906F905F904F903F90D1
+:1007F0002F90089520E030E040E05FE3C701B601AC
+:100800000E94B30E87FDE0CF2BEA3AEA4AE25FE3AB
+:10081000C701B6010E94B30E87FF1CC09301A4015B
+:10082000B5018D2F9C2F0E94460E1B012C01A701A4
+:1008300096016BEA7AEA8AE29FE30E94460E9B01E8
+:10084000AC01C201B1010E94171020E030E040EC81
+:1008500050E4B1CF5301D82DC92DB6CF2F923F927E
+:100860004F925F926F927F928F929F92AF92BF92C0
+:10087000CF92DF92EF92FF920F931F93CF93DF936C
+:10088000CDB7DEB72A970FB6F894DEBF0FBECDBF47
+:100890001C01FB0180819181A281B38189839A83AC
+:1008A000AB83BC8384809580A680B78090859A872F
+:1008B000A185A987D284C38420E030E0A901C501C5
+:1008C000B4010E94B30E882309F4C9C020E030E0CF
+:1008D000A9016A8579858D2D9C2D0E94B30E8823F0
+:1008E00009F4BDC020E030E040E05FE36A8579852F
+:1008F0008D2D9C2D0E94B30E87FF98C020E030E024
+:1009000040E85FE3C501B4010E94470E2A8539859E
+:100910004D2D5C2D0E9417104B015C012A853985F5
+:100920004D2D5C2D6A8579858D2D9C2D0E94470E5D
+:10093000A50194010E94460E2B013C012BEA3AEAE4
+:100940004AEA5EE369817A818B819C810E94470E2D
+:100950007B018C01A5019401C301B2010E948203B5
+:100960006A877987D82EC92EE980FA800B811C818D
+:10097000A5019401C301B2010E9482036D837E83AD
+:100980008F8398872BEA3AEA4AEA5EE3C801B70107
+:100990000E94460E7B018C01A5019401C301B201A6
+:1009A0000E9482034B015C0120E030E04FE753E4FA
+:1009B0006A8579858D2D9C2D0E9417100E94310F1C
+:1009C000F101608320E030E04FE753E46D817E81E8
+:1009D0008F8198850E9417100E94310FD1011196C6
+:1009E0006C9320E030E04FE753E4B401C5010E946E
+:1009F00017100E94310FF10162832A960FB6F89406
+:100A0000DEBF0FBECDBFDF91CF911F910F91FF9041
+:100A1000EF90DF90CF90BF90AF909F908F907F909E
+:100A20006F905F904F903F902F9008952A85398561
+:100A30004D2D5C2DC501B4010E94470E2B013C01D8
+:100A40002A8539854D2D5C2DC501B4010E941710F2
+:100A50009B01AC01C301B2010E94460E5DCF8A84A6
+:100A60009984AD2CBC2C8D829E82DF82C8869CCF5F
+:100A70002F923F924F925F926F927F928F92AF929E
+:100A8000BF92CF92DF92EF92FF920F931F93CF937B
+:100A9000DF937C0120E030E048EC52E4FC0160810F
+:100AA0007181828193810E9417100E942A0F5B013D
+:100AB0006091290370912A0380912B030E94020107
+:100AC0008091480290914902833C910584F0809185
+:100AD00046029091470210924902109248020196F4
+:100AE0008730910554F59093470280934602809198
+:100AF00046029091470260902003709021038090FD
+:100B0000220383309105C1F1ECF4609017037090DB
+:100B10001803809019038130910571F160901D03D5
+:100B200070901E0380901F03029731F1609026039E
+:100B300070902703809028031FC0109247021092E4
+:100B40004602D5CF60902303709024038090250344
+:100B50008530910589F060901A0370901B03809096
+:100B60001C038530910544F06090140370901503C8
+:100B7000809016030697D1F6D0E0C0E08CE7482EAF
+:100B80005524539496E6292E3324339406E411E039
+:100B9000C0904802D0904902C6018C0F9D1F833C33
+:100BA00091055CF4682DA301853591058CF50E94B3
+:100BB0001F012196C630D10559F7C50163E070E0E9
+:100BC0000E948410892B31F4C6010196909349024A
+:100BD00080934802F701868197810297C9F5809139
+:100BE000F8029091F902892B99F1DF91CF911F9131
+:100BF0000F91FF90EF90DF90CF90BF90AF908F90CC
+:100C00007F906F905F904F903F902F900C94E400F6
+:100C100022EC30E08F36910594F0E4EFF0E0873875
+:100C2000910594F020E231E08B39910544F0F80110
+:100C30008D3A91054CF08B3B91052CF49101281BCA
+:100C4000390BC901B4CFF201E81BF90BCF01AFCFCB
+:100C5000DF91CF911F910F91FF90EF90DF90CF9098
+:100C6000BF90AF908F907F906F905F904F903F902C
+:100C70002F9008958091F8029091F902892B11F03C
+:100C80000C94E40008950F931F93CF93DF93CDB797
+:100C9000DEB72F970FB6F894DEBF0FBECDBF8C0125
+:100CA000FC0180819181A281B38189839A83AB8386
+:100CB000BC8380E090E0A0E8BFE38D839E83AF8398
+:100CC000B8878DEC9CECACE4BDE389879A87AB8751
+:100CD000BC87BE016F5F7F4FCE010D960E942E0430
+:100CE0006D857E858F850E940201F801868197813E
+:100CF000029711F40E943A062F960FB6F894DEBFC1
+:100D00000FBECDBFDF91CF911F910F9108952F920C
+:100D10003F924F925F926F927F928F92BF92CF924B
+:100D2000DF92EF92FF920F931F93CF93DF938A315D
+:100D300008F051C03A01862E08E212E0D0E0C0E08F
+:100D4000C82ED12CF12CE12C8AE1B82EF80161914A
+:100D50008F0170E090E080E00E94600F20E030E0C2
+:100D600040ED51E40E94B80E1B012C01C701B601F1
+:100D70000E94620F9B01AC01C201B1010E941710D9
+:100D80001B012C0120E030E0A9010E94121020E09C
+:100D900030E040E05FE387FD2FC0C201B1010E9457
+:100DA000470E0E942A0FBC9EC001BD9E900D1124CB
+:100DB000865F9E4F680F791FFB018081882331F089
+:100DC000682DA3018150990B0E941F012196CB3001
+:100DD000D10509F0BBCFDF91CF911F910F91FF900B
+:100DE000EF90DF90CF90BF908F907F906F905F904B
+:100DF0004F903F902F900895C201B1010E94460E7E
+:100E0000D0CF4F925F926F927F92AF92BF92CF926C
+:100E1000DF92EF92FF920F931F93CF93DF93CDB7A3
+:100E2000DEB72F970FB6F894DEBF0FBECDBF5C01C3
+:100E300010E000E08DEC482E8CEC582E8CE4682EEF
+:100E40008DE3782EB801012E000C880B990B0E94BF
+:100E5000620F20E030E040ED51E40E94B80EF50151
+:100E600020813181428153810E94470E6B017C01B8
+:100E700020E030E040E85FE30E941210181654F4BE
+:100E800020E030E040E85FE3C701B6010E94460E73
+:100E90006B017C01C982DA82EB82FC8280E090E007
+:100EA000A0E8BFE38D839E83AF83B88749865A86C7
+:100EB0006B867C86BE016F5F7F4FCE010D960E94D0
+:100EC0002E044D855E856F85802F0E9487060F5FFB
+:100ED0001F4F0A31110509F0B5CFF50186819781C1
+:100EE000029711F40E943A062F960FB6F894DEBFCF
+:100EF0000FBECDBFDF91CF911F910F91FF90EF906B
+:100F0000DF90CF90BF90AF907F906F905F904F90A9
+:100F10000895CF93DF93EC0120E030E048ED51E4F9
+:100F2000688179818A819B810E9417100E942A0F13
+:100F3000CB016A3171056CF020914A0230914B026D
+:100F40002F5F3F4F27303105B4F430934B0220938D
+:100F50004A02E0914A02F0914B02E730F105C0F4F9
+:100F6000EC54F84F0C949810C007D607DD07E4073F
+:100F7000EB07F207F90710924B0210924A02E9CFF1
+:100F80004091260350912703609128030E94870611
+:100F90008E819F810297A1F58091F8029091F902CC
+:100FA000892B71F1DF91CF910C94E40040912303E0
+:100FB0005091240360912503E9CF40912003509183
+:100FC000210360912203E2CF40911D0350911E0343
+:100FD00060911F03DBCF40911A0350911B03609176
+:100FE0001C03D4CF4091170350911803609119034B
+:100FF000CDCF409114035091150360911603C6CFD5
+:10100000DF91CF9108952F923F924F925F926F920E
+:101010007F928F929F92AF92BF92CF92DF92EF9288
+:10102000FF920F931F93CF93DF93CDB7DEB72F9728
+:101030000FB6F894DEBF0FBECDBF5C018AE0C82EAC
+:1010400081E0D82E10E000E09DEC292E9CEC392E9A
+:101050009CE4492E9DE3592EB801012E000C880B0B
+:10106000990B0E94620F20E030E040E351E40E94BF
+:10107000B80EF50120813181428153810E94470ED3
+:101080003B014C0120E030E040E85FE30E94121099
+:10109000181654F420E030E040E85FE3C401B301E7
+:1010A0000E94460E3B014C0169827A828B829C82AF
+:1010B00080E090E0A0E8BFE38D839E83AF83B88794
+:1010C00029863A864B865C86BE016F5F7F4FCE01D4
+:1010D0000D960E942E046D847E848F84F12CE12C69
+:1010E000F601EE0DFF1D8081882369F0682DA301B4
+:1010F0008150990B0E941F01FFEFEF1AFF0A8AE14E
+:10110000E816F10469F70F5F1F4FEAE1CE0ED11C1C
+:101110000B30110509F0A0CFF50186819781029768
+:1011200011F40E943A062F960FB6F894DEBF0FBE58
+:10113000CDBFDF91CF911F910F91FF90EF90DF9086
+:10114000CF90BF90AF909F908F907F906F905F9067
+:101150004F903F902F9008954F925F926F927F92A1
+:101160008F929F92AF92BF92CF92DF92EF92FF92B7
+:101170000F931F93CF93DF93CDB7DEB72F970FB6A3
+:10118000F894DEBF0FBECDBFFC0186819781029728
+:1011900009F044C010E000E088EE482E83E0582EAD
+:1011A000612C712C812C912C90E8A92E9FE3B92EF3
+:1011B0002DECC22E2CECD22E2CE4E22E2DE3F22EBE
+:1011C0000E948B0DA30192010E94E30D0E94620F09
+:1011D00020E030E04AE754E40E94B80E69837A8345
+:1011E0008B839C838D829E82AF82B886C986DA8685
+:1011F000EB86FC86BE016F5F7F4FCE010D960E948D
+:101200002E044D855E856F85C8010E941F010F5F0A
+:101210001F4F023C1105A1F60E943A062F960FB609
+:10122000F894DEBF0FBECDBFDF91CF911F910F911C
+:10123000FF90EF90DF90CF90BF90AF909F908F90F6
+:101240007F906F905F904F9008951F920F920FB60E
+:101250000F9211242F933F934F935F936F937F933C
+:101260008F939F93AF93BF93EF93FF938BE592E0A0
+:101270000E94B801FF91EF91BF91AF919F918F9123
+:101280007F916F915F914F913F912F910F900FBE82
+:101290000F901F9018951F920F920FB60F92112466
+:1012A0002F938F939F93EF93FF93E0916B02F091B5
+:1012B0006C028081E0917102F091720282FD1BC08C
+:1012C0009081809174028F5F8F73209175028217D5
+:1012D00041F0E0917402F0E0E55AFD4F958F809364
+:1012E0007402FF91EF919F918F912F910F900FBEFC
+:1012F0000F901F9018958081F4CF1F920F920FB618
+:101300000F9211242F933F938F939F93AF93BF938B
+:101310008091570290915802A0915902B0915A02BF
+:1013200030914C0223E0230F2D3758F50196A11D73
+:10133000B11D20934C028093570290935802A093C2
+:101340005902B0935A0280914D0290914E02A091A1
+:101350004F02B09150020196A11DB11D80934D0224
+:1013600090934E02A0934F02B0935002BF91AF9161
+:101370009F918F913F912F910F900FBE0F901F90D3
+:10138000189526E8230F0296A11DB11DD2CF109209
+:101390005E0210925D0288EE93E0A0E0B0E08093E0
+:1013A0005F0290936002A0936102B093620287E3B0
+:1013B00092E090935C0280935B0285EC90E09093C6
+:1013C00068028093670284EC90E090936A028093B5
+:1013D000690280EC90E090936C0280936B0281EC48
+:1013E00090E090936E0280936D0222EC30E0309397
+:1013F000700220936F0286EC90E0909372028093CB
+:10140000710210927402109275021092760210927C
+:1014100077021092290310922A0310922B03109244
+:1014200026038CE380932703109228039CE090937B
+:10143000230380932403109225039EE190932003BD
+:10144000909321031092220380931D0310921E0398
+:1014500010921F0390931A0310921B0380931C0396
+:10146000109217031092180380931903109214031B
+:10147000909315038093160330930603209305037E
+:101480001092070386E492E0909309038093080387
+:1014900083E180930A0310921203109211031092B9
+:1014A000130361E00E947002809108039091090388
+:1014B0000E94B2109093100380930F03E1ECF0E0D0
+:1014C000E491F0E0EE0FFF1FEC55FF4F85919491F2
+:1014D0009093120380931103E3EAF0E0E491E09328
+:1014E00013038AE090E09093F9028093F80210923F
+:1014F000FC021092FD021092FE021092FF02109266
+:1015000001031092000381E08093040381E090E0E6
+:1015100090930303809302038CE390E00E94B21047
+:10152000FC019C01245C3F4F1182108213821282C5
+:10153000158214823696E217F307B1F79093FB02F7
+:101540008093FA020895CF93DF93CDB7DEB72E973D
+:101550000FB6F894DEBF0FBECDBF789484B582601D
+:1015600084BD84B5816084BD85B5826085BD85B547
+:10157000816085BD80916E00816080936E001092C5
+:101580008100809181008260809381008091810040
+:1015900081608093810080918000816080938000D1
+:1015A0008091B10084608093B1008091B00081602F
+:1015B0008093B00080917A00846080937A0080915B
+:1015C0007A00826080937A0080917A0081608093B3
+:1015D0007A0080917A00806880937A001092C1002E
+:1015E00062E08EE00E947002E0916B02F0916C026A
+:1015F00082E08083E0916702F09168021082E091BE
+:101600006902F0916A0280E1808310927302E09196
+:101610006F02F091700286E08083E0916D02F0919C
+:101620006E02808180618083E0916D02F0916E0294
+:10163000808188608083E0916D02F0916E028081EC
+:1016400080688083E0916D02F0916E0280818F7DD1
+:1016500080830E94F1006C01990FEE08FF0861E0A1
+:1016600070E080E090E00E94500303E010E00E94F0
+:10167000F100002E02C0880F991F0A94E2F74C0176
+:10168000990FAA08BB08240135014C245D246E245F
+:101690007F2461E070E080E090E00E9450030D5FE5
+:1016A0001F4F0132110509F0E1C08C149D04AE04F6
+:1016B000BF0421F0C301B2010E94DA0D20910A0398
+:1016C00030E0F901E458FF4F8491F901E057FF4FF2
+:1016D0000491F901E255FF4F1491112391F081110A
+:1016E0000E944702E12FF0E0EE0FFF1FEC55FF4F85
+:1016F000A591B4918FB7F894EC9100950E230C93BB
+:101700008FBF0E94AC0260930B0370930C03809315
+:101710000D0390930E0360E070E080E00E940201F0
+:101720006091290370912A0380912B030E9402018A
+:101730000E94D10280910403882309F4C4C08FB7AA
+:10174000F894C0905702D0905802E0905902F0905F
+:101750005A028FBF8091FC029091FD02A091FE027F
+:10176000B091FF02A7019601281B390B4A0B5B0BB6
+:10177000CA01B901209102033091030350E040E017
+:10178000621773078407950708F49DC00E94150E21
+:1017900049015A011C861B8610E000E08091F80286
+:1017A0009091F9020817190708F085C02090FA02F5
+:1017B0003090FB024B855C85240E351ED1011496BA
+:1017C0000D90BC91A02DBE87AD871E830D83F101C6
+:1017D00082819381AC0170E060E084169506A606D4
+:1017E000B70608F046C0208131813A87298721E079
+:1017F00030E049855A858417950711F430E020E0E0
+:1018000038872F8369857A85681B790B90E080E0A3
+:101810000E94600F2B013C01A985BA85BD0190E0B3
+:1018200080E00E94600F9B01AC01C301B2010E94E5
+:10183000B80E69837A838B839C83CE010196ED85F4
+:10184000FE850995D10112968D919C911397881967
+:10185000990913969C938E9312970F5F1F4F2B85B8
+:101860003C852A5F3F4F3C872B8798CF73016201ED
+:10187000FECE892B91F3E2E0F0E0F887EF8320E0E1
+:1018800030E040E85FE329833A834B835C838091B7
+:10189000000390910103019790930103809300034B
+:1018A000D10113961C921E921297CE010196ED85DE
+:1018B000FE850995D2CFC092FC02D092FD02E09243
+:1018C000FE02F092FF020E94D1026AE070E080E026
+:1018D00090E00E945003EAE8F0E08491EEE9F0E045
+:1018E0001491ECEBF0E00491002309F45DC0811148
+:1018F0000E944702E02FF0E0EE0FFF1FEE58FF4F6F
+:10190000A591B4918C91182309F44EC08091530293
+:1019100090915402A0915502B09156020097A105F2
+:10192000B10509F05CC02FB7F894809157029091EF
+:101930005802A0915902B0915A022FBF80935302CE
+:1019400090935402A0935502B09356028091510295
+:10195000909152028230910509F46FC084F400978F
+:1019600009F45FC0019709F463C08091510290911E
+:1019700052020196909352028093510245C08430E6
+:10198000910509F464C00CF45DC0059771F76CEA29
+:1019900078E084E690E00E948D008FEF9FEF9093B7
+:1019A000520280935102E1CF609126037091270388
+:1019B000809128030E9402010E94D10268EE73E028
+:1019C00080E090E00E9450036091290370912A0307
+:1019D00080912B030E9402010E94D102FFCF2FB7FA
+:1019E000F8944091570250915802609159027091B9
+:1019F0005A022FBF8857934DAF4FBF4F841795079B
+:101A0000A607B70708F48FCF80E090E0892B09F490
+:101A100091CE0E94A401882309F48CCE0E9400007C
+:101A200089CE68E375E084EF91E00E948D009DCF40
+:101A300069E877E088E592E0F8CF61E077E088EB4D
+:101A40009BE0F3CF63E078E080ED97E0EECF63E4D6
+:101A500076E080E49FE1E9CF8091FA029091FB0269
+:101A6000009711F00E944E1160E080910A030E94DD
+:101A7000700280910F03909110030C944E118F927D
+:101A80009F92AF92BF92CF92DF92EF92FF92CF934D
+:101A9000DF93EC01688179818A819B8161157105F1
+:101AA0008105910521F464E279ED8BE597E02DE164
+:101AB00033EF41E050E00E94E30D49015A019B01E0
+:101AC000AC01A7EAB1E40E94020E6B017C01ACEE0E
+:101AD000B4EFA50194010E94100EC60ED71EE81E99
+:101AE000F91EF7FE06C081E0C81AD108E10880E8B7
+:101AF000F80AC882D982EA82FB82C701B6019F77C1
+:101B0000DF91CF91FF90EF90DF90CF90BF90AF909B
+:101B10009F908F9008958F929F92AF92BF92CF9295
+:101B2000DF92EF92FF92609100017091010180912C
+:101B3000020190910301611571058105910521F460
+:101B400064E279ED8BE597E02DE133EF41E050E081
+:101B50000E94E30D49015A019B01AC01A7EAB1E4DF
+:101B60000E94020E6B017C01ACEEB4EFA501940162
+:101B70000E94100EC60ED71EE81EF91EF7FE06C004
+:101B800081E0C81AD108E10880E8F80AC092000193
+:101B9000D0920101E0920201F0920301C701B60167
+:101BA0009F77FF90EF90DF90CF90BF90AF909F9086
+:101BB0008F9008956093000170930101809302015A
+:101BC000909303010895052E97FB1EF400940E9444
+:101BD000FA0D57FD07D00E94150E07FC03D04EF4F6
+:101BE0000C94FA0D50954095309521953F4F4F4FED
+:101BF0005F4F089590958095709561957F4F8F4FB9
+:101C00009F4F08950E94370EA59F900DB49F900D91
+:101C1000A49F800D911D11240895B7FF0C94020E0E
+:101C20000E94020E821B930B0895A1E21A2EAA1B9A
+:101C3000BB1BFD010DC0AA1FBB1FEE1FFF1FA2177C
+:101C4000B307E407F50720F0A21BB30BE40BF50B79
+:101C5000661F771F881F991F1A9469F76095709502
+:101C6000809590959B01AC01BD01CF010895A29F85
+:101C7000B001B39FC001A39F700D811D1124911D60
+:101C8000B29F700D811D1124911D08955058BB27DE
+:101C9000AA270E945E0E0C94D80F0E94CA0F38F03B
+:101CA0000E94D10F20F039F49F3F19F426F40C94D0
+:101CB000C70F0EF4E095E7FB0C94C10FE92F0E94CB
+:101CC000E90F58F3BA17620773078407950720F0E6
+:101CD00079F4A6F50C940B100EF4E0950B2EBA2FA8
+:101CE000A02D0B01B90190010C01CA01A001112422
+:101CF000FF27591B99F0593F50F4503E68F11A16CE
+:101D0000F040A22F232F342F4427585FF3CF46955E
+:101D100037952795A795F0405395C9F77EF41F1680
+:101D2000BA0B620B730B840BBAF09150A1F0FF0F4A
+:101D3000BB1F661F771F881FC2F70EC0BA0F621F36
+:101D4000731F841F48F4879577956795B795F79526
+:101D50009E3F08F0B0CF9395880F08F09927EE0FBB
+:101D60009795879508950E949D0F08F481E0089546
+:101D70000E94CC0E0C94D80F0E94D10F58F00E94F4
+:101D8000CA0F40F029F45F3F29F00C94C10F5111A4
+:101D90000C940C100C94C70F0E94E90F68F3992360
+:101DA000B1F3552391F3951B550BBB27AA27621757
+:101DB0007307840738F09F5F5F4F220F331F441F64
+:101DC000AA1FA9F335D00E2E3AF0E0E832D0915098
+:101DD0005040E695001CCAF72BD0FE2F29D0660F85
+:101DE000771F881FBB1F261737074807AB07B0E8C8
+:101DF00009F0BB0B802DBF01FF2793585F4F3AF0CE
+:101E00009E3F510578F00C94C10F0C940C105F3F6D
+:101E1000E4F3983ED4F3869577956795B795F79553
+:101E20009F5FC9F7880F911D9695879597F908953B
+:101E3000E1E0660F771F881FBB1F621773078407D7
+:101E4000BA0720F0621B730B840BBA0BEE1F88F7E6
+:101E5000E09508950E94310F6894B1110C940C1014
+:101E600008950E94F10F88F09F5798F0B92F992795
+:101E7000B751B0F0E1F0660F771F881F991F1AF075
+:101E8000BA95C9F714C0B13091F00E940B10B1E0BF
+:101E900008950C940B10672F782F8827B85F39F0BE
+:101EA000B93FCCF3869577956795B395D9F73EF40E
+:101EB00090958095709561957F4F8F4F9F4F0895B6
+:101EC000E89409C097FB3EF49095809570956195D4
+:101ED0007F4F8F4F9F4F9923A9F0F92F96E9BB278A
+:101EE0009395F695879577956795B795F111F8CF06
+:101EF000FAF4BB0F11F460FF1BC06F5F7F4F8F4F71
+:101F00009F4F16C0882311F096E911C0772321F066
+:101F10009EE8872F762F05C0662371F096E8862FFE
+:101F200070E060E02AF09A95660F771F881FDAF755
+:101F3000880F9695879597F90895990F0008550F82
+:101F4000AA0BE0E8FEEF16161706E807F907C0F03F
+:101F500012161306E407F50798F0621B730B840B47
+:101F6000950B39F40A2661F0232B242B252B21F421
+:101F700008950A2609F4A140A6958FEF811D811DC1
+:101F8000089597F99F6780E870E060E008959FEFFB
+:101F900080EC089500240A94161617061806090600
+:101FA000089500240A9412161306140605060895CF
+:101FB000092E0394000C11F4882352F0BB0F40F457
+:101FC000BF2B11F460FF04C06F5F7F4F8F4F9F4F97
+:101FD000089557FD9058440F551F59F05F3F71F019
+:101FE0004795880F97FB991F61F09F3F79F0879580
+:101FF0000895121613061406551FF2CF4695F1DF09
+:1020000008C0161617061806991FF1CF8695710598
+:10201000610508940895E894BB2766277727CB01CC
+:1020200097F908950E949D0F08F48FEF08950E947C
+:102030002A100C94D80F0E94CA0F38F00E94D10FBA
+:1020400020F0952311F00C94C10F0C94C70F1124AC
+:102050000C940C100E94E90F70F3959FC1F3950F3B
+:1020600050E0551F629FF001729FBB27F00DB11D1C
+:10207000639FAA27F00DB11DAA1F649F6627B00DAC
+:10208000A11D661F829F2227B00DA11D621F739F95
+:10209000B00DA11D621F839FA00D611D221F749FA3
+:1020A0003327A00D611D231F849F600D211D822FEA
+:1020B000762F6A2F11249F5750409AF0F1F0882311
+:1020C0004AF0EE0FFF1FBB1F661F771F881F91503E
+:1020D0005040A9F79E3F510580F00C94C10F0C941D
+:1020E0000C105F3FE4F3983ED4F38695779567959F
+:1020F000B795F795E7959F5FC1F7FE2B880F911D68
+:102100009695879597F9089597FB072E16F40094F6
+:1021100007D077FD09D00E949E1007FC05D03EF441
+:10212000909581959F4F0895709561957F4F089583
+:10213000EE0FFF1F0590F491E02D0994AA1BBB1B25
+:1021400051E107C0AA1FBB1FA617B70710F0A61BB7
+:10215000B70B881F991F5A95A9F780959095BC01D8
+:10216000CD0108950F931F93CF93DF938230910594
+:1021700010F482E090E0E0912E03F0912F0330E024
+:1021800020E0B0E0A0E0309799F42115310509F482
+:102190004AC0281B390B24303105D8F58A819B8130
+:1021A0006115710589F1FB0193838283FE0111C0E2
+:1021B000408151810281138148175907E0F0481787
+:1021C000590799F4109761F012960C931297139691
+:1021D0001C933296CF01DF91CF911F910F910895FB
+:1021E00000932E0310932F03F4CF2115310551F0E6
+:1021F0004217530738F0A901DB019A01BD01DF0145
+:10220000F801C1CFEF01F9CF90932F0380932E03F4
+:10221000CDCFFE01E20FF31F81939193225031093C
+:1022200039832883D7CF20912C0330912D03232B82
+:1022300041F4209106013091070130932D03209342
+:102240002C0320910401309105012115310541F441
+:102250002DB73EB74091080150910901241B350B61
+:10226000E0912C03F0912D03E217F307A0F42E1B4D
+:102270003F0B2817390778F0AC014E5F5F4F2417EA
+:10228000350748F04E0F5F1F50932D0340932C03EA
+:10229000819391939FCFF0E0E0E09CCFCF93DF93C9
+:1022A0000097E9F0FC01329713821282A0912E036D
+:1022B000B0912F03ED0130E020E01097A1F42081D0
+:1022C0003181820F931F20912C0330912D03281709
+:1022D000390709F061C0F0932D03E0932C03DF91DF
+:1022E000CF910895EA01CE17DF07E8F54A815B81B7
+:1022F0009E0141155105B1F7E901FB83EA8349913C
+:102300005991C40FD51FEC17FD0761F480819181AD
+:102310000296840F951FE9019983888382819381B6
+:102320009B838A83F0E0E0E012968D919C91139755
+:102330000097B9F52D913C911197CD010296820F2E
+:10234000931F20912C0330912D032817390739F65C
+:10235000309751F510922F0310922E03B0932D0356
+:10236000A0932C03BCCFD383C28340815181840FBF
+:10237000951FC817D90761F44E5F5F4F8881998117
+:10238000480F591F518340838A819B8193838283A5
+:102390002115310509F0B0CFF0932F03E0932E0300
+:1023A0009ECFFD01DC01C0CF13821282D7CF10E097
+:1023B000C2E6D0E004C0FE010E9498102196C33608
+:1023C000D107C9F7F894FFCF0100000000003003E7
+:1023D0008000C2C1C0BFBEBDBCBB00000000000089
+:1023E000000000000000000000000000BAB9B8B70B
+:1023F000B6B5B4B3B2B1B0AFAEAD000000000000EE
+:10240000000000000000ACABAAA9A8A7A6A5A4A341
+:10241000A2A1A09F9E9D9C9B0000000000000000C8
+:102420009A999897969594939291908F8E8D8C8B84
+:102430008A89888700000000000086858483828165
+:10244000807F7E7D7C7B7A79787776757473727104
+:10245000706F00006E6D6C6B6A69686766656463B7
+:102460006261605F5E5D5C5B5A59585756553D3EF0
+:102470003F404142434445464748494A4B4C4D4EF4
+:102480004F50515253540000292A2B2C2D2E2F30FF
+:102490003132333435363738393A3B3C00000000AE
+:1024A00000001718191A1B1C1D1E1F20212223248F
+:1024B000252627280000000000000000090A0B0C58
+:1024C0000D0E0F101112131415160000000000005D
+:1024D00000000000000001020304050607080000D8
+:1024E00000000000000000000000000000000000EC
+:1024F000080E1214181A1814120E0800000000DA40
+:0E250000013A0167012702980176018A010065
+:107E0000112484B714BE81FFF0D085E080938100F7
+:107E100082E08093C00088E18093C10086E0809377
+:107E2000C20080E18093C4008EE0C9D0259A86E02C
+:107E300020E33CEF91E0309385002093840096BBD3
+:107E4000B09BFECF1D9AA8958150A9F7CC24DD24C4
+:107E500088248394B5E0AB2EA1E19A2EF3E0BF2EE7
+:107E6000A2D0813461F49FD0082FAFD0023811F036
+:107E7000013811F484E001C083E08DD089C08234E0
+:107E800011F484E103C0853419F485E0A6D080C0E4
+:107E9000853579F488D0E82EFF2485D0082F10E0AE
+:107EA000102F00270E291F29000F111F8ED06801E7
+:107EB0006FC0863521F484E090D080E0DECF843638
+:107EC00009F040C070D06FD0082F6DD080E0C81688
+:107ED00080E7D80618F4F601B7BEE895C0E0D1E017
+:107EE00062D089930C17E1F7F0E0CF16F0E7DF06D8
+:107EF00018F0F601B7BEE89568D007B600FCFDCFD4
+:107F0000A601A0E0B1E02C9130E011968C91119780
+:107F100090E0982F8827822B932B1296FA010C0160
+:107F200087BEE89511244E5F5F4FF1E0A038BF0790
+:107F300051F7F601A7BEE89507B600FCFDCF97BE46
+:107F4000E89526C08437B1F42ED02DD0F82E2BD052
+:107F50003CD0F601EF2C8F010F5F1F4F84911BD097
+:107F6000EA94F801C1F70894C11CD11CFA94CF0C13
+:107F7000D11C0EC0853739F428D08EE10CD085E9AC
+:107F80000AD08FE07ACF813511F488E018D01DD067
+:107F900080E101D065CF982F8091C00085FFFCCF94
+:107FA0009093C60008958091C00087FFFCCF809118
+:107FB000C00084FD01C0A8958091C6000895E0E648
+:107FC000F0E098E1908380830895EDDF803219F02E
+:107FD00088E0F5DFFFCF84E1DECF1F93182FE3DFCA
+:107FE0001150E9F7F2DF1F91089580E0E8DFEE27F6
+:047FF000FF270994CA
+:027FFE00040479
+:00000001FF

二进制
ring.stl


二进制
templateA.stl


二进制
templateB.stl