2018-02-18 14:11:55 +00:00
|
|
|
# MicroPython apds9960 motion detection device driver.
|
|
|
|
|
2018-02-28 17:51:45 +00:00
|
|
|
#NOTE: This code is derived from a mix of Sparkfun and Adafruit arduino drivers.
|
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
#todo: Have a mix of styles here. Some use properties for setters while others use functions.
|
2018-01-25 20:43:55 +00:00
|
|
|
|
|
|
|
import pyb
|
2018-02-18 14:11:55 +00:00
|
|
|
from utime import *
|
|
|
|
|
|
|
|
def setvalue( aOriginal, aPosBits, aValue ) :
|
|
|
|
'''Set a specific bit on a variable. aPosBits = 0xpm (p = pos, m = # mask bits).'''
|
|
|
|
mask = (1 << (aPosBits & 0xf)) - 1
|
|
|
|
pos = aPosBits >> 4
|
|
|
|
aOriginal &= ~(mask << pos)
|
|
|
|
return aOriginal | (aValue & mask) << pos
|
|
|
|
|
|
|
|
def getvalue( aOriginal, aPosBits ) :
|
|
|
|
'''Get a specific bit from a variable. aPosBits = 0xpm (p = pos, m = # mask bits).'''
|
|
|
|
mask = (1 << (aPosBits & 0xf)) - 1
|
|
|
|
pos = aPosBits >> 4
|
|
|
|
return (aOriginal >> pos) & mask
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
class apds9960(object) :
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
_ADDRESS = const(0x39)
|
2018-01-25 20:43:55 +00:00
|
|
|
_RAM = const(0x00)
|
|
|
|
_ENABLE = const(0x80)
|
|
|
|
_ATIME = const(0x81)
|
|
|
|
_WTIME = const(0x83)
|
|
|
|
_AILTL = const(0x84)
|
|
|
|
_AILTH = const(0x85)
|
|
|
|
_AIHTL = const(0x86)
|
|
|
|
_AIHTH = const(0x87)
|
|
|
|
_PILT = const(0x89)
|
|
|
|
_PIHT = const(0x8B)
|
|
|
|
_PERS = const(0x8C)
|
|
|
|
_CONFIG1 = const(0x8D)
|
|
|
|
_PPULSE = const(0x8E)
|
|
|
|
_CONTROL = const(0x8F)
|
|
|
|
_CONFIG2 = const(0x90)
|
|
|
|
_ID = const(0x92)
|
|
|
|
_STATUS = const(0x93)
|
|
|
|
_CDATAL = const(0x94)
|
|
|
|
_CDATAH = const(0x95)
|
|
|
|
_RDATAL = const(0x96)
|
|
|
|
_RDATAH = const(0x97)
|
|
|
|
_GDATAL = const(0x98)
|
|
|
|
_GDATAH = const(0x99)
|
|
|
|
_BDATAL = const(0x9A)
|
|
|
|
_BDATAH = const(0x9B)
|
|
|
|
_PDATA = const(0x9C)
|
|
|
|
_POFFSET_UR = const(0x9D)
|
|
|
|
_POFFSET_DL = const(0x9E)
|
|
|
|
_CONFIG3 = const(0x9F)
|
|
|
|
_GPENTH = const(0xA0)
|
|
|
|
_GEXTH = const(0xA1)
|
|
|
|
_GCONF1 = const(0xA2)
|
|
|
|
_GCONF2 = const(0xA3)
|
|
|
|
_GOFFSET_U = const(0xA4)
|
|
|
|
_GOFFSET_D = const(0xA5)
|
|
|
|
_GOFFSET_L = const(0xA7)
|
|
|
|
_GOFFSET_R = const(0xA9)
|
|
|
|
_GPULSE = const(0xA6)
|
|
|
|
_GCONF3 = const(0xAA)
|
|
|
|
_GCONF4 = const(0xAB)
|
|
|
|
_GFLVL = const(0xAE)
|
|
|
|
_GSTATUS = const(0xAF)
|
|
|
|
_IFORCE = const(0xE4)
|
|
|
|
_PICLEAR = const(0xE5)
|
|
|
|
_CICLEAR = const(0xE6)
|
|
|
|
_AICLEAR = const(0xE7)
|
|
|
|
_GFIFO_U = const(0xFC)
|
|
|
|
_GFIFO_D = const(0xFD)
|
|
|
|
_GFIFO_L = const(0xFE)
|
|
|
|
_GFIFO_R = const(0xFF)
|
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
#Device IDs
|
|
|
|
_ID_1 = const(0xAB)
|
|
|
|
_ID_2 = const(0x9C)
|
|
|
|
|
2018-01-25 20:43:55 +00:00
|
|
|
#A Gain
|
2018-02-18 14:11:55 +00:00
|
|
|
_AGAIN_1X = const(0) #No gain
|
|
|
|
_AGAIN_4X = const(1) #2x gain
|
|
|
|
_AGAIN_16X = const(2) #16x gain
|
|
|
|
_AGAIN_64X = const(3) #64x gain
|
2018-01-25 20:43:55 +00:00
|
|
|
|
|
|
|
#P Gain
|
2018-02-18 14:11:55 +00:00
|
|
|
_PGAIN_1X = const(0) #1x gain
|
|
|
|
_PGAIN_2X = const(1) #2x gain
|
|
|
|
_PGAIN_4X = const(2) #4x gain
|
|
|
|
_PGAIN_8X = const(3) #8x gain
|
2018-01-25 20:43:55 +00:00
|
|
|
|
|
|
|
#Pulse Length
|
2018-02-18 14:11:55 +00:00
|
|
|
_PPULSELEN_4US = const(0) #4uS
|
|
|
|
_PPULSELEN_8US = const(1) #8uS
|
|
|
|
_PPULSELEN_16US = const(2) #16uS
|
|
|
|
_PPULSELEN_32US = const(3) #32uS
|
2018-01-25 20:43:55 +00:00
|
|
|
|
|
|
|
#LED Drive
|
2018-02-18 14:11:55 +00:00
|
|
|
_LEDDRIVE_100MA = const(0) #100mA
|
|
|
|
_LEDDRIVE_50MA = const(1) #50mA
|
|
|
|
_LEDDRIVE_25MA = const(2) #25mA
|
|
|
|
_LEDDRIVE_12MA = const(3) #12.5mA
|
2018-01-25 20:43:55 +00:00
|
|
|
|
|
|
|
#LED Boost
|
2018-02-18 14:11:55 +00:00
|
|
|
_LEDBOOST_100 = const(0) #100%
|
|
|
|
_LEDBOOST_150 = const(1) #150%
|
|
|
|
_LEDBOOST_200 = const(2) #200%
|
|
|
|
_LEDBOOST_300 = const(3) #300%
|
2018-01-25 20:43:55 +00:00
|
|
|
|
|
|
|
#Dimensions
|
2018-02-18 14:11:55 +00:00
|
|
|
_DIMENSIONS_ALL = const(0)
|
|
|
|
_DIMENSIONS_UP_DOWM = const(1)
|
|
|
|
_DIMENSIONS_LEFT_RIGHT = const(2)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
|
|
|
#FIFO
|
2018-02-18 14:11:55 +00:00
|
|
|
_GFIFO_1 = const(0)
|
|
|
|
_GFIFO_4 = const(1)
|
|
|
|
_GFIFO_8 = const(2)
|
|
|
|
_GFIFO_16 = const(3)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
|
|
|
#G Gain
|
2018-02-18 14:11:55 +00:00
|
|
|
_GGAIN_1 = const(0)
|
|
|
|
_GGAIN_2 = const(1)
|
|
|
|
_GGAIN_4 = const(2)
|
|
|
|
_GGAIN_8 = const(3)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
|
|
|
#G Pulse
|
2018-02-18 14:11:55 +00:00
|
|
|
_GPULSE_4US = const(0)
|
|
|
|
_GPULSE_8US = const(1)
|
|
|
|
_GPULSE_16US = const(2)
|
|
|
|
_GPULSE_32US = const(3)
|
|
|
|
|
2018-02-28 17:51:45 +00:00
|
|
|
#Value Location data.
|
2018-02-18 14:11:55 +00:00
|
|
|
#Following are the bytes describing the data packed into each 8 bit value on the chip.
|
|
|
|
#Nibble 1 = position, 0 = bits
|
2018-02-28 17:51:45 +00:00
|
|
|
#NOTE: Commented out currently unused locations.
|
2018-02-18 14:11:55 +00:00
|
|
|
|
2018-02-28 17:51:45 +00:00
|
|
|
_ENABLE_PON = const(0x01)
|
|
|
|
_ENABLE_AEN = const(0x11)
|
|
|
|
_ENABLE_PEN = const(0x21)
|
|
|
|
# _ENABLE_WEN = const(0x31)
|
|
|
|
# _ENABLE_AIEN = const(0x41)
|
|
|
|
_ENABLE_PIEN = const(0x51)
|
|
|
|
_ENABLE_GEN = const(0x61)
|
2018-02-18 14:11:55 +00:00
|
|
|
|
2018-02-28 17:51:45 +00:00
|
|
|
# _PERS_APERS = const(0x04)
|
|
|
|
_PERS_PPERS = const(0x44)
|
2018-02-18 14:11:55 +00:00
|
|
|
|
2018-02-28 17:51:45 +00:00
|
|
|
_PPULSE_PPULSE = const(0x06)
|
|
|
|
_PPULSE_PPLEN = const(0x62)
|
2018-02-18 14:11:55 +00:00
|
|
|
|
2018-02-28 17:51:45 +00:00
|
|
|
_CONTROL_AGAIN = const(0x02)
|
|
|
|
_CONTROL_PGAIN = const(0x22)
|
|
|
|
_CONTROL_LDRIVE = const(0x42)
|
2018-02-18 14:11:55 +00:00
|
|
|
|
2018-02-28 17:51:45 +00:00
|
|
|
_CONFIG2_LED_BOOST = const(0x42)
|
|
|
|
# _CONFIG2_CPSIEN = const(0x61) #Clear photo diode saturation int enable.
|
|
|
|
# _CONFIG2_PSIEN = const(0x71) #Proximity saturation interrupt enable.
|
2018-02-18 14:11:55 +00:00
|
|
|
|
2018-02-28 17:51:45 +00:00
|
|
|
# _CONFIG3_PMASK_R = const(0x01)
|
|
|
|
# _CONFIG3_PMASK_L = const(0x11)
|
|
|
|
# _CONFIG3_PMASK_D = const(0x21)
|
|
|
|
# _CONFIG3_PMASK_U = const(0x31)
|
|
|
|
# _CONFIG3_SAI = const(0x41)
|
|
|
|
# _CONFIG3_PCMP = const(0x51)
|
2018-02-18 14:11:55 +00:00
|
|
|
|
2018-02-28 17:51:45 +00:00
|
|
|
# _GCONFIG1_GEXPERS = const(0x02)
|
|
|
|
# _GCONFIG1_GEXMSK = const(0x24)
|
|
|
|
_GCONFIG1_GFIFOTH = const(0x62)
|
2018-02-18 14:11:55 +00:00
|
|
|
|
2018-02-28 17:51:45 +00:00
|
|
|
_GCONFIG2_GWTIME = const(0x03)
|
|
|
|
# _GCONFIG2_GLDRIVE = const(0x32)
|
|
|
|
_GCONFIG2_GGAIN = const(0x52)
|
2018-02-18 14:11:55 +00:00
|
|
|
|
2018-02-28 17:51:45 +00:00
|
|
|
_GCONFIG3_GDIMS = const(0x02)
|
2018-02-18 14:11:55 +00:00
|
|
|
|
2018-02-28 17:51:45 +00:00
|
|
|
# _GCONFIG4_GMODE = const(0x01)
|
|
|
|
# _GCONFIG4_GIEN = const(0x12)
|
2018-02-18 14:11:55 +00:00
|
|
|
|
2018-02-28 17:51:45 +00:00
|
|
|
_GPULSE_GPULSE = const(0x06)
|
|
|
|
_GPULSE_GPLEN = const(0x62)
|
2018-02-18 14:11:55 +00:00
|
|
|
|
2018-02-28 17:51:45 +00:00
|
|
|
_STATUS_AVALID = const(0x01)
|
|
|
|
# _STATUS_PVALID = const(0x11)
|
|
|
|
# _STATUS_GINT = const(0x21)
|
|
|
|
# _STATUS_AINT = const(0x41)
|
|
|
|
# _STATUS_PINT = const(0x51)
|
|
|
|
# _STATUS_PGSTAT = const(0x61)
|
|
|
|
# _STATUS_CPSTAT = const(0x71)
|
2018-02-18 14:11:55 +00:00
|
|
|
|
2018-02-28 17:51:45 +00:00
|
|
|
_GSTATUS_GVALID = const(0x01)
|
|
|
|
# _GSTATUS_GFOV = const(0x11)
|
2018-02-18 14:11:55 +00:00
|
|
|
|
|
|
|
# Gesture wait time values
|
|
|
|
_GWTIME_0MS = const(0)
|
|
|
|
_GWTIME_2_8MS = const(1)
|
|
|
|
_GWTIME_5_6MS = const(2)
|
|
|
|
_GWTIME_8_4MS = const(3)
|
|
|
|
_GWTIME_14_0MS = const(4)
|
|
|
|
_GWTIME_22_4MS = const(5)
|
|
|
|
_GWTIME_30_8MS = const(6)
|
|
|
|
_GWTIME_39_2MS = const(7)
|
|
|
|
|
2018-02-26 15:31:37 +00:00
|
|
|
_THRESHOLD_OUT = const(10)
|
|
|
|
_SENSITIVITY_1 = const(50)
|
|
|
|
_SENSITIVITY_2 = const(20)
|
|
|
|
|
|
|
|
_DIR_NONE = const(0)
|
|
|
|
_DIR_UP = const(1)
|
|
|
|
_DIR_DOWN = const(2)
|
|
|
|
_DIR_LEFT = const(3)
|
|
|
|
_DIR_RIGHT = const(4)
|
|
|
|
_DIR_NEAR = const(5)
|
|
|
|
_DIR_FAR = const(6)
|
|
|
|
_DIR_ALL = const(7)
|
|
|
|
|
|
|
|
_NA_STATE = const(0)
|
|
|
|
_NEAR_STATE = const(1)
|
|
|
|
_FAR_STATE = const(2)
|
|
|
|
_ALL_STATE = const(3)
|
2018-02-18 14:11:55 +00:00
|
|
|
|
|
|
|
_DefaultIntTimeMS = const(10)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
|
|
|
def __init__( self, aLoc ) :
|
2018-02-28 17:51:45 +00:00
|
|
|
'''aLoc I2C pin location is either 1, 'X', 2 or'Y'.'''
|
2018-02-18 14:11:55 +00:00
|
|
|
self._i2c = pyb.I2C(aLoc, pyb.I2C.MASTER)
|
|
|
|
|
2018-02-28 17:51:45 +00:00
|
|
|
#Mirrors data on the controller in order to eliminate need to read data from the controller on modification.
|
2018-02-18 14:11:55 +00:00
|
|
|
self._enable = 0 #Enable
|
|
|
|
self._pers = 0 #Pers
|
|
|
|
self._ppulse = 0 #PPulse
|
|
|
|
self._gpulse = 0 #GPulse
|
|
|
|
self._control = 0 #Control
|
|
|
|
self._config2 = 0 #Config 2.
|
|
|
|
self._config3 = 0 #Config 3.
|
|
|
|
self._gconfig1 = 0 #Gesture Config 1.
|
|
|
|
self._gconfig2 = 0 #Gesture Config 2
|
|
|
|
self._gconfig3 = 0 #Gesture Config 3
|
|
|
|
self._gconfig4 = 0 #Gesture Config 4.
|
|
|
|
self._status = 0 #Status
|
|
|
|
self._gstatus = 0 #GStatus
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-26 15:31:37 +00:00
|
|
|
#gesture read variables.
|
|
|
|
self._ud_delta = 0
|
|
|
|
self._lr_delta = 0
|
|
|
|
self._ud_count = 0
|
|
|
|
self._lr_count = 0
|
|
|
|
self._near_count = 0
|
|
|
|
self._far_count = 0
|
|
|
|
self._state = 0
|
|
|
|
self._motion = _DIR_NONE
|
|
|
|
self._udata = bytearray(32)
|
|
|
|
self._ddata = bytearray(32)
|
|
|
|
self._ldata = bytearray(32)
|
|
|
|
self._rdata = bytearray(32)
|
|
|
|
self._index = 0
|
|
|
|
self._total_gestures= 0
|
|
|
|
self._fifo_data = bytearray(128)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-26 15:31:37 +00:00
|
|
|
self.resetcounts()
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
self._b1 = bytearray(1)
|
|
|
|
self._b2 = bytearray(2)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
|
|
|
sleep_us(50)
|
|
|
|
self.begin()
|
|
|
|
|
|
|
|
def read( self, aLoc ) :
|
2018-02-28 17:51:45 +00:00
|
|
|
'''Read 8 bit value and return.'''
|
2018-02-18 14:11:55 +00:00
|
|
|
self._i2c.mem_read(self._b1, _ADDRESS, aLoc)
|
|
|
|
return self._b1[0]
|
|
|
|
|
2018-02-26 15:31:37 +00:00
|
|
|
def readbuffer( self, aBuffer, aLoc ) :
|
2018-02-28 17:51:45 +00:00
|
|
|
'''Read 8 bit values into given buffer.'''
|
2018-02-26 15:31:37 +00:00
|
|
|
self._i2c.mem_read(aBuffer, _ADDRESS, aLoc)
|
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
def read16( self, aLoc ) :
|
2018-02-28 17:51:45 +00:00
|
|
|
'''Read 16 bit value and return.'''
|
2018-02-18 14:11:55 +00:00
|
|
|
self._i2c.mem_read(self._b2, _ADDRESS, aLoc)
|
|
|
|
return (self._b2[1] << 8) | self._b2[0]
|
|
|
|
|
|
|
|
def write( self, aLoc, aVal ) :
|
2018-02-28 17:51:45 +00:00
|
|
|
'''Write 8 bit value to given address. aVal may be a byte array.'''
|
2018-02-18 14:11:55 +00:00
|
|
|
self._i2c.mem_write(aVal, _ADDRESS, aLoc)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
def write16( self, aLoc, aVal ) :
|
2018-02-28 17:51:45 +00:00
|
|
|
'''Write 16 bit value to given address.'''
|
2018-02-18 14:11:55 +00:00
|
|
|
self._b2[0] = aVal
|
|
|
|
self._b2[1] = aVal >> 8
|
|
|
|
self._i2c.mem_write(self._b2, _ADDRESS, aLoc)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
|
|
|
def reset( self ):
|
2018-02-28 17:51:45 +00:00
|
|
|
'''Reset the controller and set default frequency.'''
|
2018-01-25 20:43:55 +00:00
|
|
|
self.write(0, _MODE1)
|
|
|
|
self.setfreq(_DEFAULTFREQ)
|
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
#Enable
|
|
|
|
def setenable( self, aMember, aValue ) :
|
|
|
|
self._enable = setvalue(self._enable, aMember, aValue)
|
|
|
|
self.write(_ENABLE, self._enable)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
def getenable( self, aMember ) :
|
|
|
|
return getvalue(self._enable, aMember)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
#Pers
|
|
|
|
def setpers( self, aMember, aValue ) :
|
|
|
|
self._pers = setvalue(self._pers, aMember, aValue)
|
|
|
|
self.write(_PERS, self._pers)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
def getpers( self, aMember ) :
|
|
|
|
return getvalue(self._pers, aMember)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
#Control
|
|
|
|
def setcontrol( self, aMember, aValue ) :
|
|
|
|
self._control = setvalue(self._control, aMember, aValue)
|
|
|
|
self.write(_CONTROL, self._control)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
def getcontrol( self, aMember ) :
|
|
|
|
return getvalue(self._control, aMember)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
#Config 3
|
|
|
|
def setconfig2( self, aMember, aValue ) :
|
|
|
|
self._config2 = setvalue(self._config2, aMember, aValue)
|
|
|
|
self.write(_CONFIG2, self._config2)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
def getconfig2( self, aMember ) :
|
|
|
|
return getvalue(self._config2, aMember)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
#Config 3
|
|
|
|
def setconfig3( self, aMember, aValue ) :
|
|
|
|
self._config3 = setvalue(self._config3, aMember, aValue)
|
|
|
|
self.write(_CONFIG3, self._config3)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
def getconfig3( self, aMember ) :
|
|
|
|
return getvalue(self._config3, aMember)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
#GConfig 1
|
|
|
|
def setgconfig1( self, aMember, aValue ) :
|
|
|
|
self._gconfig1 = setvalue(self._gconfig1, aMember, aValue)
|
|
|
|
self.write(_GCONF1, self._gconfig1)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
def getgconfig1( self, aMember ) :
|
|
|
|
return getvalue(self._gconfig1, aMember)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
#GConfig 2
|
|
|
|
def setgconfig2( self, aMember, aValue ) :
|
|
|
|
self._gconfig2 = setvalue(self._gconfig2, aMember, aValue)
|
|
|
|
self.write(_GCONF2, self._gconfig2)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
def getgconfig2( self, aMember ) :
|
|
|
|
return getvalue(self._gconfig2, aMember)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
#GConfig 3
|
|
|
|
def setgconfig3( self, aMember, aValue ) :
|
|
|
|
self._gconfig3 = setvalue(self._gconfig3, aMember, aValue)
|
|
|
|
self.write(_GCONF3, self._gconfig3)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
def getgconfig3( self, aMember ) :
|
|
|
|
return getvalue(self._gconfig3, aMember)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
#GConfig 4
|
|
|
|
def setgconfig4( self, aMember, aValue ) :
|
|
|
|
self._gconfig4 = setvalue(self._gconfig4, aMember, aValue)
|
|
|
|
self.write(_GCONF4, self._gconfig4)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
def getgconfig4( self, aMember ) :
|
|
|
|
return getvalue(self._gconfig4, aMember)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
#Status
|
|
|
|
def getstatus( self, aMember ) :
|
|
|
|
'''Get member of status'''
|
|
|
|
self._status = self.read(_STATUS)
|
|
|
|
return getvalue(self._status, aMember)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
#GStatus
|
|
|
|
def getgstatus( self, aMember ) :
|
2018-02-28 17:51:45 +00:00
|
|
|
'''Get member of gstatus'''
|
2018-02-18 14:11:55 +00:00
|
|
|
self._gstatus = self.read(_GSTATUS)
|
|
|
|
return getvalue(self._gstatus, aMember)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
def resetcounts( self ) :
|
2018-02-28 17:51:45 +00:00
|
|
|
'''Reset the gesture reading counters.'''
|
2018-02-18 14:11:55 +00:00
|
|
|
self._gestCnt = 0
|
|
|
|
#todo - put these in an array.
|
|
|
|
self._ucount = 0
|
|
|
|
self._dcount = 0
|
|
|
|
self._lcount = 0
|
|
|
|
self._rcount = 0
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
def enableproximityint( self, aValue ) :
|
2018-02-28 17:51:45 +00:00
|
|
|
self.setenable(_ENABLE_PIEN, aValue)
|
2018-02-18 14:11:55 +00:00
|
|
|
self.clearInterrupt()
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
def proximityintthreshold( self, aValue, aPersistance ) :
|
|
|
|
self.write(_PILT, aValue & 0xFF)
|
|
|
|
self.write(_PIHT, aValue >> 8)
|
2018-02-28 17:51:45 +00:00
|
|
|
self.setpers(_PERS_PPERS, min(aPersistance, 7))
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
def proximity( self ) :
|
|
|
|
return self.read(_PDATA)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
def gestureenterthreshold( self, aValue ) :
|
|
|
|
self.write(_GPENTH, aValue)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
def gestureexitthreshold( self, aValue ) :
|
|
|
|
self.write(_GEXTH, aValue)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
|
|
|
def gestureoffset( self, aUp, aDown, aLeft, aRight ) :
|
2018-02-18 14:11:55 +00:00
|
|
|
self.write(_GOFFSET_U, aUp)
|
|
|
|
self.write(_GOFFSET_D, aDown)
|
|
|
|
self.write(_GOFFSET_L, aLeft)
|
|
|
|
self.write(_GOFFSET_R, aRight)
|
|
|
|
|
|
|
|
def enablegesture( self, aEnable ) :
|
|
|
|
#If disabling make sure we aren't currently in gesture mode.
|
|
|
|
# Gesture mode is auto entered when proximity enter threshold is hit
|
2018-02-28 17:51:45 +00:00
|
|
|
# so we don't need to enable it even though we disable on gesture disable.
|
2018-01-25 20:43:55 +00:00
|
|
|
if not aEnable :
|
2018-02-18 14:11:55 +00:00
|
|
|
self.setgconfig4(GCONFIG4_GMODE, False)
|
2018-02-28 17:51:45 +00:00
|
|
|
self.setenable(_ENABLE_GEN, aEnable)
|
2018-02-18 14:11:55 +00:00
|
|
|
self.resetcounts()
|
2018-01-25 20:43:55 +00:00
|
|
|
|
|
|
|
@property
|
2018-02-18 14:11:55 +00:00
|
|
|
def gesturevalid( self ) :
|
2018-02-28 17:51:45 +00:00
|
|
|
'''Return True if gesture data exists.'''
|
|
|
|
return self.getgstatus(_GSTATUS_GVALID)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
def gesturedata( self ) :
|
2018-02-28 17:51:45 +00:00
|
|
|
'''Read gesture data and return as (u,d,l,r). Returns None of no data ready.'''
|
2018-02-18 14:11:55 +00:00
|
|
|
level = self.read(_GFLVL)
|
|
|
|
if level == 0 :
|
|
|
|
return # no data
|
|
|
|
fifo_u = self.read(_GFIFO_U)
|
|
|
|
fifo_d = self.read(_GFIFO_D)
|
|
|
|
fifo_l = self.read(_GFIFO_L)
|
|
|
|
fifo_r = self.read(_GFIFO_R)
|
|
|
|
|
|
|
|
return (fifo_u, fifo_d, fifo_l, fifo_r)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def colordataready( self ) :
|
2018-02-28 17:51:45 +00:00
|
|
|
'''Return true if color data ready to read.'''
|
|
|
|
return self.getstatus(_STATUS_AVALID)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
|
|
|
def colordata( self ) :
|
2018-02-28 17:51:45 +00:00
|
|
|
'''Return color data as (c,r,g,b).'''
|
2018-02-18 14:11:55 +00:00
|
|
|
c = self.read16(_CDATAL)
|
|
|
|
r = self.read16(_RDATAL)
|
|
|
|
g = self.read16(_GDATAL)
|
|
|
|
b = self.read16(_BDATAL)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
|
|
|
return (c, r, g, b)
|
|
|
|
|
|
|
|
def clearInterrupt( self ) :
|
|
|
|
#NOTE: The .ccp writes 0 bytes to this address. I don't know how
|
|
|
|
# to do that so I'm writing a 0 to AICLEAR.
|
2018-02-18 14:11:55 +00:00
|
|
|
self.write(_AICLEAR, 0)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
|
|
|
def intlimits( self, aLow, aHigh ) :
|
2018-02-18 14:11:55 +00:00
|
|
|
self.write16(_AILTL, aLow)
|
|
|
|
self.write16(_AIHTL, aHigh)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
|
|
|
def adcintegrationtime( self, aValue ) :
|
2018-02-18 14:11:55 +00:00
|
|
|
t = max(0.0, min(255.0, 256.0 - (aValue / 2.78)))
|
|
|
|
self.write(_ATIME, int(t))
|
2018-01-25 20:43:55 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def enabled( self ) :
|
2018-02-28 17:51:45 +00:00
|
|
|
return self.getenable(_ENABLE_PON)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
|
|
|
@enabled.setter
|
|
|
|
def enabled( self, aValue ) :
|
2018-02-28 17:51:45 +00:00
|
|
|
self.setenable(_ENABLE_PON, aValue)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
|
|
|
def proxpulse( self, aLen, aPulses ) :
|
2018-02-28 17:51:45 +00:00
|
|
|
setvalue(self._ppulse, _PPULSE_PPULSE, min(64, max(aPulses, 1)) - 1)
|
|
|
|
setvalue(self._ppulse, _PPULSE_PPLEN, aLen)
|
|
|
|
self.write(_PPULSE_PPULSE, self._ppulse)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
def gpulse( self, aLen, aPulses ) :
|
2018-02-28 17:51:45 +00:00
|
|
|
setvalue(self._gpulse, _GPULSE_GPULSE, min(64, max(aPulses, 1)) - 1)
|
|
|
|
setvalue(self._gpulse, _GPULSE_GPLEN, aLen)
|
|
|
|
self.write(_GPULSE_GPULSE, self._gpulse)
|
2018-01-25 20:43:55 +00:00
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
def begin( self ) :
|
2018-02-28 17:51:45 +00:00
|
|
|
'''Initialize the board for use.'''
|
2018-02-18 14:11:55 +00:00
|
|
|
id = self.read(_ID)
|
|
|
|
if id != _ID_1 and id != _ID_2:
|
|
|
|
raise Exception('Incorrect apds9960 ID {}.'.format(id))
|
2018-01-25 20:43:55 +00:00
|
|
|
|
|
|
|
self.enabled = False
|
|
|
|
sleep_us(50)
|
2018-02-18 14:11:55 +00:00
|
|
|
|
|
|
|
# Set default integration time and gain
|
|
|
|
self.adcintegrationtime(_DefaultIntTimeMS)
|
|
|
|
self.write(_WTIME, 249)
|
|
|
|
self.write(_POFFSET_UR, 0)
|
|
|
|
self.write(_POFFSET_DL, 0)
|
|
|
|
self.write(_CONFIG1, 0x60)
|
2018-02-28 17:51:45 +00:00
|
|
|
self.setcontrol(_CONTROL_LDRIVE, _LEDDRIVE_100MA)
|
|
|
|
self.setcontrol(_CONTROL_AGAIN, _AGAIN_16X)
|
|
|
|
self.setcontrol(_CONTROL_PGAIN, _PGAIN_8X)
|
2018-02-18 14:11:55 +00:00
|
|
|
|
|
|
|
self.proxpulse(_PPULSELEN_16US, 10)
|
|
|
|
self.gpulse(_GPULSE_32US, 9)
|
|
|
|
|
|
|
|
self.proximityintthreshold(0x3200, 1)
|
|
|
|
self.enableproximityint(False) #Proximity Interrupt
|
|
|
|
|
|
|
|
#100 makes the results a bit more erratic, No value boosts distance. It seems
|
|
|
|
# the higher the boost the more stable the data is.
|
2018-02-28 17:51:45 +00:00
|
|
|
self.setconfig2(_CONFIG2_LED_BOOST, _LEDBOOST_300)
|
2018-02-18 14:11:55 +00:00
|
|
|
|
|
|
|
#NOTE: This must be 1 if enter threshold is < exit or we will never get gesture input because we
|
|
|
|
# won't stay in the gesture state for more than 1 cycle.
|
2018-02-28 17:51:45 +00:00
|
|
|
self.setgconfig1(_GCONFIG1_GFIFOTH, _GFIFO_1)
|
|
|
|
self.setgconfig2(_GCONFIG2_GWTIME, _GWTIME_2_8MS)
|
|
|
|
self.setgconfig2(_GCONFIG2_GGAIN, _GGAIN_4)
|
|
|
|
self.setgconfig3(_GCONFIG3_GDIMS, _DIMENSIONS_ALL)
|
2018-02-18 14:11:55 +00:00
|
|
|
#NOTE: An enter value lower than the exit value means we are constantly entering/exiting the state machine.
|
|
|
|
# If we don't do this we will not get proximity and color input because those are not updated when in gesture state.
|
|
|
|
self.gestureenterthreshold(5) #When proximity value is above this we enter the gesture state machine.
|
|
|
|
self.gestureexitthreshold(255) #When all gesture values are below this we exit the gesture state machine.
|
|
|
|
self.gestureoffset(0, 0, 0, 0)
|
|
|
|
|
2018-02-28 17:51:45 +00:00
|
|
|
self.setenable(_ENABLE_PEN, True) #enable proximity
|
|
|
|
self.setenable(_ENABLE_AEN, True) #enable color
|
2018-02-18 14:11:55 +00:00
|
|
|
self.enablegesture(True)
|
|
|
|
|
2018-01-25 20:43:55 +00:00
|
|
|
self.enabled = True
|
|
|
|
sleep_us(50)
|
|
|
|
|
2018-02-26 15:31:37 +00:00
|
|
|
def resetgestureparams( self ) :
|
2018-02-28 17:51:45 +00:00
|
|
|
#NOTE: This function could probably be merged with resetcounts().
|
2018-02-26 15:31:37 +00:00
|
|
|
self._index = 0
|
|
|
|
self._total_gestures = 0
|
|
|
|
self._ud_delta = 0
|
|
|
|
self._lr_delta = 0
|
|
|
|
self._ud_count = 0
|
|
|
|
self._lr_count = 0
|
|
|
|
self._near_count = 0
|
|
|
|
self._far_count = 0
|
|
|
|
self._state = 0
|
|
|
|
self._motion = _DIR_NONE
|
|
|
|
|
2018-02-18 14:11:55 +00:00
|
|
|
def readgesture( self ) :
|
2018-02-28 17:51:45 +00:00
|
|
|
'''Read gesture data and process for up, down, left, right movement.
|
|
|
|
Returns _DIR_???.'''
|
|
|
|
#NOTE: This function currently doesn't seem to return very accurate results.
|
2018-02-26 15:31:37 +00:00
|
|
|
if self.gesturevalid :
|
|
|
|
while True :
|
|
|
|
sleep_us(30) #Wait a bit to make sure fifo buffer is full.
|
|
|
|
|
2018-02-28 17:51:45 +00:00
|
|
|
gstatus = self.getgstatus(_GSTATUS_GVALID)
|
2018-02-26 15:31:37 +00:00
|
|
|
if gstatus :
|
|
|
|
fifo_level = self.read(_GFLVL)
|
|
|
|
if fifo_level > 0 :
|
|
|
|
bread = fifo_level * 4
|
|
|
|
self.readbuffer(self._fifo_data, _GFIFO_U)
|
|
|
|
|
|
|
|
for i in range(0, fifo_level):
|
|
|
|
i2 = i * 4
|
|
|
|
self._udata[i] = self._fifo_data[i2]
|
|
|
|
self._ddata[i] = self._fifo_data[i2 + 1]
|
|
|
|
self._ldata[i] = self._fifo_data[i2 + 2]
|
|
|
|
self._rdata[i] = self._fifo_data[i2 + 3]
|
|
|
|
|
|
|
|
self._index += fifo_level - 1
|
|
|
|
self._total_gestures += fifo_level - 1
|
|
|
|
|
|
|
|
if self.processgesturedata() :
|
|
|
|
self._index = 0
|
|
|
|
self._total_gestures = 0
|
|
|
|
else:
|
|
|
|
sleep_us(30)
|
|
|
|
self.decodegesture()
|
|
|
|
self.resetgestureparams()
|
|
|
|
return self._motion
|
|
|
|
|
|
|
|
return _DIR_NONE
|
|
|
|
|
|
|
|
def processgesturedata( self ) :
|
|
|
|
if self._total_gestures > 4 :
|
|
|
|
ufirst = 0
|
|
|
|
dfirst = 0
|
|
|
|
lfirst = 0
|
|
|
|
rfirst = 0
|
|
|
|
ulast = 0
|
|
|
|
dlast = 0
|
|
|
|
llast = 0
|
|
|
|
rlast = 0
|
|
|
|
|
|
|
|
if 0 < self._total_gestures <= 32 :
|
|
|
|
for i in range(0, self._total_gestures) :
|
|
|
|
u = self._udata[i]
|
|
|
|
d = self._ddata[i]
|
|
|
|
l = self._ldata[i]
|
|
|
|
r = self._rdata[i]
|
|
|
|
if u > _THRESHOLD_OUT and d > _THRESHOLD_OUT and l > _THRESHOLD_OUT and r > _THRESHOLD_OUT :
|
|
|
|
ufirst = u
|
|
|
|
dfirst = d
|
|
|
|
lfirst = l
|
|
|
|
rfirst = r
|
|
|
|
break
|
|
|
|
|
|
|
|
if ufirst == 0 or dfirst == 0 or lfirst == 0 or rfirst == 0 :
|
|
|
|
return False
|
|
|
|
|
|
|
|
for i in range(self._total_gestures - 1, -1, -1) :
|
|
|
|
u = self._udata[i]
|
|
|
|
d = self._ddata[i]
|
|
|
|
l = self._ldata[i]
|
|
|
|
r = self._rdata[i]
|
|
|
|
if u > _THRESHOLD_OUT and d > _THRESHOLD_OUT and l > _THRESHOLD_OUT and r > _THRESHOLD_OUT :
|
|
|
|
ulast = u
|
|
|
|
dlast = d
|
|
|
|
llast = l
|
|
|
|
rlast = r
|
|
|
|
break
|
|
|
|
|
|
|
|
ud_ratio_first = ((ufirst - dfirst) * 100) // (ufirst + dfirst)
|
|
|
|
lr_ratio_first = ((lfirst - rfirst) * 100) // (lfirst + rfirst)
|
|
|
|
ud_ratio_last = ((ulast - dlast) * 100) // (ulast + dlast)
|
|
|
|
lr_ratio_last = ((llast - rlast) * 100) // (llast + rlast)
|
|
|
|
|
|
|
|
ud_delta = ud_ratio_last - ud_ratio_first
|
|
|
|
lr_delta = lr_ratio_last - lr_ratio_first
|
|
|
|
|
|
|
|
self._ud_delta += ud_delta
|
|
|
|
self._lr_delta += lr_delta
|
|
|
|
|
|
|
|
if self._ud_delta >= _SENSITIVITY_1 :
|
|
|
|
self._ud_count = 1
|
|
|
|
elif self._ud_delta <= -_SENSITIVITY_1 :
|
|
|
|
self._ud_count = -1
|
|
|
|
else:
|
|
|
|
self._ud_count = 0
|
|
|
|
|
|
|
|
if self._lr_delta >= _SENSITIVITY_1 :
|
|
|
|
self._lr_count = 1
|
|
|
|
elif self._lr_delta <= -_SENSITIVITY_1 :
|
|
|
|
self._lr_count = -1
|
|
|
|
else:
|
|
|
|
self._lr_count = 0
|
|
|
|
|
|
|
|
if self._ud_count == 0 and self._lr_count == 0 :
|
|
|
|
if abs(ud_delta) < _SENSITIVITY_2 and abs(lr_delta) < _SENSITIVITY_2 :
|
|
|
|
if ud_delta == 0 and lr_delta == 0 :
|
|
|
|
self._near_count += 1
|
|
|
|
elif ud_delta != 0 and lr_delta != 0 :
|
|
|
|
self._far_count += 1
|
|
|
|
|
|
|
|
if self._near_count >= 10 and self._far_count >= 2 :
|
|
|
|
if ud_delta == 0 and lr_delta == 0 :
|
|
|
|
self._state = _NEAR_STATE
|
|
|
|
elif ud_delta != 0 and lr_delta != 0 :
|
|
|
|
self._state = _FAR_STATE
|
|
|
|
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
if abs(ud_delta) < _SENSITIVITY_2 and abs(lr_delta) < _SENSITIVITY_2 :
|
|
|
|
if ud_delta == 0 and lr_delta == 0 :
|
|
|
|
self._near_count += 1
|
|
|
|
|
|
|
|
if self._near_count >= 10 :
|
|
|
|
self._ud_count = 0
|
|
|
|
self._lr_count = 0
|
|
|
|
self._ud_delta = 0
|
|
|
|
self._lr_delta = 0
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
def decodegesture( self ) :
|
|
|
|
if self._state == _NEAR_STATE :
|
|
|
|
self._motion = _DIR_NEAR
|
|
|
|
return True
|
|
|
|
elif self._state == _FAR_STATE :
|
|
|
|
self._motion = _DIR_FAR
|
|
|
|
return True
|
|
|
|
|
|
|
|
if self._ud_count == -1 and self._lr_count == 0 :
|
|
|
|
self._motion = _DIR_UP
|
|
|
|
elif self._ud_count == 1 and self._lr_count == 0 :
|
|
|
|
self._motion = _DIR_DOWN
|
|
|
|
elif self._ud_count == 0 and self._lr_count == 1 :
|
|
|
|
self._motion == _DIR_RIGHT
|
|
|
|
elif self._ud_count == 0 and self._lr_count == -1 :
|
|
|
|
self._motion == _DIR_LEFT
|
|
|
|
elif self._ud_count == -1 and self._lr_count == 1 :
|
|
|
|
if abs(self._ud_delta) > abs(self._lr_delta) :
|
|
|
|
self._motion = _DIR_UP
|
|
|
|
else:
|
|
|
|
self._motion = _DIR_RIGHT
|
|
|
|
elif self._ud_count == 1 and self._lr_count == -1 :
|
|
|
|
if abs(self._ud_delta) > abs(self._lr_delta) :
|
|
|
|
self._motion = _DIR_DOWN
|
|
|
|
else:
|
|
|
|
self._motion = _DIR_LEFT
|
|
|
|
elif self._ud_count == -1 and self._lr_count == -1 :
|
|
|
|
if abs(self._ud_delta) > abs(self._lr_delta) :
|
|
|
|
self._motion = _DIR_UP
|
|
|
|
else:
|
|
|
|
self._motion = _DIR_LEFT
|
|
|
|
elif self._ud_count == 1 and self._lr_count == 1 :
|
|
|
|
if abs(self._ud_delta) > abs(self._lr_delta) :
|
|
|
|
self._motion = _DIR_DOWN
|
|
|
|
else:
|
|
|
|
self._motion = _DIR_RIGHT
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
|
|
|
|
return True
|
2018-02-18 14:11:55 +00:00
|
|
|
|
|
|
|
# def calculatecolortemp( self, aRed, aGreen, aBlue ) :
|
|
|
|
# x = (-0.14282 * aRed) + (1.54924 * aGreen) + (-0.95641 * aBlue)
|
|
|
|
# y = self.calculatelux(aRed, aGreen, aBlue)
|
|
|
|
# z = (-0.68202 * aRed) + (0.77073 * aGreen) + (0.56332 * aBlue)
|
|
|
|
#
|
|
|
|
# den = x + y + z
|
|
|
|
# xc = x / den
|
|
|
|
# yc = y / den
|
|
|
|
#
|
|
|
|
# n = (xc - 0.332) / (0.1858 - yc)
|
|
|
|
#
|
|
|
|
# cct = (449.0 * pow(n, 3)) + (35250 * pow(n, 2)) + (6823.3 * n) + 5520.33
|
|
|
|
#
|
|
|
|
# return cct
|
|
|
|
#
|
|
|
|
# def calculatelux( self, aRed, aGreen, aBlue ) :
|
|
|
|
# return (-0.32466 * aRed) + (1.57837 * aGreen) + (-0.73191 * aBlue)
|
2018-01-25 20:43:55 +00:00
|
|
|
|