gobot.io/x/gobot/v2@v2.1.0/drivers/i2c/tsl2561_driver.go (about)

     1  package i2c
     2  
     3  import (
     4  	"fmt"
     5  	"time"
     6  )
     7  
     8  const (
     9  	// TSL2561AddressLow - the address of the device when address pin is low
    10  	TSL2561AddressLow = 0x29
    11  	// TSL2561AddressFloat - the address of the device when address pin is floating
    12  	TSL2561AddressFloat = 0x39
    13  	// TSL2561AddressHigh - the address of the device when address pin is high
    14  	TSL2561AddressHigh = 0x49
    15  
    16  	tsl2561CommandBit = 0x80 // Must be 1
    17  	tsl2561ClearBit   = 0x40 // Clears any pending interrupt (write 1 to clear)
    18  	tsl2561WordBit    = 0x20 // 1 = read/write word (rather than byte)
    19  	tsl2561BlockBit   = 0x10 // 1 = using block read/write
    20  
    21  	tsl2561ControlPowerOn  = 0x03
    22  	tsl2561ControlPowerOff = 0x00
    23  
    24  	tsl2561LuxLuxScale     = 14     // Scale by 2^14
    25  	tsl2561LuxRatioScale   = 9      // Scale ratio by 2^9
    26  	tsl2561LuxChScale      = 10     // Scale channel values by 2^10
    27  	tsl2561LuxCHScaleTInt0 = 0x7517 // 322/11 * 2^tsl2561LUXCHSCALE
    28  	tsl2561LuxChScaleTInt1 = 0x0FE7 // 322/81 * 2^tsl2561LUXCHSCALE
    29  
    30  	// T, FN and CL package values
    31  	tsl2561LuxK1T = 0x0040 // 0.125 * 2^RATIO_SCALE
    32  	tsl2561LuxB1T = 0x01f2 // 0.0304 * 2^LUX_SCALE
    33  	tsl2561LuxM1T = 0x01be // 0.0272 * 2^LUX_SCALE
    34  	tsl2561LuxK2T = 0x0080 // 0.250 * 2^RATIO_SCALE
    35  	tsl2561LuxB2T = 0x0214 // 0.0325 * 2^LUX_SCALE
    36  	tsl2561LuxM2T = 0x02d1 // 0.0440 * 2^LUX_SCALE
    37  	tsl2561LuxK3T = 0x00c0 // 0.375 * 2^RATIO_SCALE
    38  	tsl2561LuxB3T = 0x023f // 0.0351 * 2^LUX_SCALE
    39  	tsl2561LuxM3T = 0x037b // 0.0544 * 2^LUX_SCALE
    40  	tsl2561LuxK4T = 0x0100 // 0.50 * 2^RATIO_SCALE
    41  	tsl2561LuxB4T = 0x0270 // 0.0381 * 2^LUX_SCALE
    42  	tsl2561LuxM4T = 0x03fe // 0.0624 * 2^LUX_SCALE
    43  	tsl2561LuxK5T = 0x0138 // 0.61 * 2^RATIO_SCALE
    44  	tsl2561LuxB5T = 0x016f // 0.0224 * 2^LUX_SCALE
    45  	tsl2561LuxM5T = 0x01fc // 0.0310 * 2^LUX_SCALE
    46  	tsl2561LuxK6T = 0x019a // 0.80 * 2^RATIO_SCALE
    47  	tsl2561LuxB6T = 0x00d2 // 0.0128 * 2^LUX_SCALE
    48  	tsl2561LuxM6T = 0x00fb // 0.0153 * 2^LUX_SCALE
    49  	tsl2561LuxK7T = 0x029a // 1.3 * 2^RATIO_SCALE
    50  	tsl2561LuxB7T = 0x0018 // 0.00146 * 2^LUX_SCALE
    51  	tsl2561LuxM7T = 0x0012 // 0.00112 * 2^LUX_SCALE
    52  	tsl2561LuxK8T = 0x029a // 1.3 * 2^RATIO_SCALE
    53  	tsl2561LuxB8T = 0x0000 // 0.000 * 2^LUX_SCALE
    54  	tsl2561LuxM8T = 0x0000 // 0.000 * 2^LUX_SCALE
    55  
    56  	// Auto-gain thresholds
    57  	tsl2561AgcTHi13MS  = 4850 // Max value at Ti 13ms = 5047
    58  	tsl2561AgcTLo13MS  = 100
    59  	tsl2561AgcTHi101MS = 36000 // Max value at Ti 101ms = 37177
    60  	tsl2561AgcTLo101MS = 200
    61  	tsl2561AgcTHi402MS = 63000 // Max value at Ti 402ms = 65535
    62  	tsl2561AgcTLo402MS = 500
    63  
    64  	// Clipping thresholds
    65  	tsl2561Clipping13MS  = 4900
    66  	tsl2561Clipping101MS = 37000
    67  	tsl2561Clipping402MS = 65000
    68  )
    69  
    70  const (
    71  	tsl2561RegisterControl         = 0x00
    72  	tsl2561RegisterTiming          = 0x01
    73  	tsl2561RegisterThreshholdLLow  = 0x02
    74  	tsl2561RegisterThreshholdLHigh = 0x03
    75  	tsl2561RegisterThreshholdHLow  = 0x04
    76  	tsl2561RegisterThreshholdHHigh = 0x05
    77  	tsl2561RegisterInterrupt       = 0x06
    78  	tsl2561RegisterCRC             = 0x08
    79  	tsl2561RegisterID              = 0x0A
    80  	tsl2561RegisterChan0Low        = 0x0C
    81  	tsl2561RegisterChan0High       = 0x0D
    82  	tsl2561RegisterChan1Low        = 0x0E
    83  	tsl2561RegisterChan1High       = 0x0F
    84  )
    85  
    86  // TSL2561IntegrationTime is the type of all valid integration time settings
    87  type TSL2561IntegrationTime int
    88  
    89  const (
    90  	// TSL2561IntegrationTime13MS integration time 13ms
    91  	TSL2561IntegrationTime13MS TSL2561IntegrationTime = iota // 13.7ms
    92  	// TSL2561IntegrationTime101MS integration time 101ms
    93  	TSL2561IntegrationTime101MS // 101ms
    94  	// TSL2561IntegrationTime402MS integration time 402ms
    95  	TSL2561IntegrationTime402MS // 402ms
    96  )
    97  
    98  // TSL2561Gain is the type of all valid gain settings
    99  type TSL2561Gain int
   100  
   101  const (
   102  	// TSL2561Gain1X gain == 1x
   103  	TSL2561Gain1X TSL2561Gain = 0x00 // No gain
   104  	// TSL2561Gain16X gain == 16x
   105  	TSL2561Gain16X = 0x10 // 16x gain
   106  )
   107  
   108  // TSL2561Driver is the gobot driver for the Adafruit Digital Luminosity/Lux/Light Sensor
   109  //
   110  // Datasheet: http://www.adafruit.com/datasheets/TSL2561.pdf
   111  //
   112  // Ported from the Adafruit driver at https://github.com/adafruit/Adafruit_TSL2561 by
   113  // K. Townsend
   114  type TSL2561Driver struct {
   115  	*Driver
   116  	autoGain        bool
   117  	gain            TSL2561Gain
   118  	integrationTime TSL2561IntegrationTime
   119  }
   120  
   121  // NewTSL2561Driver creates a new driver for the TSL2561 device.
   122  //
   123  // Params:
   124  //		c Connector - the Adaptor to use with this Driver
   125  //
   126  // Optional params:
   127  //		i2c.WithBus(int):		bus to use with this driver
   128  //		i2c.WithAddress(int):		address to use with this driver
   129  //		i2c.WithTSL2561Gain1X:		sets the gain to 1X
   130  //		i2c.WithTSL2561Gain16X:		sets the gain to 16X
   131  //		i2c.WithTSL2561AutoGain:	turns on auto gain
   132  //		i2c.WithTSL2561IntegrationTime13MS:	sets integration time to 13ms
   133  //		i2c.WithTSL2561IntegrationTime101MS: 	sets integration time to 101ms
   134  //		i2c.WithTSL2561IntegrationTime402MS: 	sets integration time to 402ms
   135  //
   136  func NewTSL2561Driver(c Connector, options ...func(Config)) *TSL2561Driver {
   137  	d := &TSL2561Driver{
   138  		Driver:          NewDriver(c, "TSL2561", TSL2561AddressFloat),
   139  		integrationTime: TSL2561IntegrationTime402MS,
   140  		gain:            TSL2561Gain1X,
   141  		autoGain:        false,
   142  	}
   143  	d.afterStart = d.initialize
   144  
   145  	for _, option := range options {
   146  		option(d)
   147  	}
   148  
   149  	return d
   150  }
   151  
   152  // WithTSL2561Gain1X option sets the TSL2561Driver gain to 1X
   153  func WithTSL2561Gain1X(c Config) {
   154  	d, ok := c.(*TSL2561Driver)
   155  	if ok {
   156  		d.gain = TSL2561Gain1X
   157  		return
   158  	}
   159  	// TODO: return errors.New("Trying to set Gain for non-TSL2561Driver")
   160  }
   161  
   162  // WithTSL2561Gain16X option sets the TSL2561Driver gain to 16X
   163  func WithTSL2561Gain16X(c Config) {
   164  	d, ok := c.(*TSL2561Driver)
   165  	if ok {
   166  		d.gain = TSL2561Gain16X
   167  		return
   168  	}
   169  	// TODO: return errors.New("Trying to set Gain for non-TSL2561Driver")
   170  }
   171  
   172  // WithTSL2561AutoGain option turns on TSL2561Driver auto gain
   173  func WithTSL2561AutoGain(c Config) {
   174  	d, ok := c.(*TSL2561Driver)
   175  	if ok {
   176  		d.autoGain = true
   177  		return
   178  	}
   179  	// TODO: return errors.New("Trying to set Auto Gain for non-TSL2561Driver")
   180  }
   181  
   182  func withTSL2561IntegrationTime(iTime TSL2561IntegrationTime) func(Config) {
   183  	return func(c Config) {
   184  		d, ok := c.(*TSL2561Driver)
   185  		if ok {
   186  			d.integrationTime = iTime
   187  			return
   188  		}
   189  		// TODO: return errors.New("Trying to set integration time for non-TSL2561Driver")
   190  	}
   191  }
   192  
   193  // WithTSL2561IntegrationTime13MS option sets the TSL2561Driver integration time
   194  // to 13ms
   195  func WithTSL2561IntegrationTime13MS(c Config) {
   196  	withTSL2561IntegrationTime(TSL2561IntegrationTime13MS)(c)
   197  }
   198  
   199  // WithTSL2561IntegrationTime101MS option sets the TSL2561Driver integration time
   200  // to 101ms
   201  func WithTSL2561IntegrationTime101MS(c Config) {
   202  	withTSL2561IntegrationTime(TSL2561IntegrationTime101MS)(c)
   203  }
   204  
   205  // WithTSL2561IntegrationTime402MS option sets the TSL2561Driver integration time
   206  // to 402ms
   207  func WithTSL2561IntegrationTime402MS(c Config) {
   208  	withTSL2561IntegrationTime(TSL2561IntegrationTime402MS)(c)
   209  }
   210  
   211  // SetIntegrationTime sets integrations time for the TSL2561
   212  func (d *TSL2561Driver) SetIntegrationTime(time TSL2561IntegrationTime) error {
   213  	if err := d.enable(); err != nil {
   214  		return err
   215  	}
   216  
   217  	timeGainVal := uint8(time) | uint8(d.gain)
   218  	if err := d.connection.WriteByteData(tsl2561CommandBit|tsl2561RegisterTiming, timeGainVal); err != nil {
   219  		return err
   220  	}
   221  	d.integrationTime = time
   222  
   223  	return d.disable()
   224  }
   225  
   226  // SetGain adjusts the TSL2561 gain (sensitivity to light)
   227  func (d *TSL2561Driver) SetGain(gain TSL2561Gain) error {
   228  	if err := d.enable(); err != nil {
   229  		return err
   230  	}
   231  
   232  	timeGainVal := uint8(d.integrationTime) | uint8(gain)
   233  	if err := d.connection.WriteByteData(tsl2561CommandBit|tsl2561RegisterTiming, timeGainVal); err != nil {
   234  		return err
   235  	}
   236  	d.gain = gain
   237  
   238  	return d.disable()
   239  }
   240  
   241  // GetLuminocity gets the broadband and IR only values from the TSL2561,
   242  // adjusting gain if auto-gain is enabled
   243  func (d *TSL2561Driver) GetLuminocity() (broadband uint16, ir uint16, err error) {
   244  	// if auto gain disabled get a single reading and continue
   245  	if !d.autoGain {
   246  		broadband, ir, err = d.getData()
   247  		return
   248  	}
   249  
   250  	agcCheck := false
   251  	hi, lo := d.getHiLo()
   252  
   253  	// Read data until we find a valid range
   254  	valid := false
   255  	for {
   256  		broadband, ir, err = d.getData()
   257  		if err != nil {
   258  			return
   259  		}
   260  
   261  		// Run an auto-gain check if we haven't already done so
   262  		if !agcCheck {
   263  			if (broadband < lo) && (d.gain == TSL2561Gain1X) {
   264  				// increase gain and try again
   265  				err = d.SetGain(TSL2561Gain16X)
   266  				if err != nil {
   267  					return
   268  				}
   269  				agcCheck = true
   270  			} else if (broadband > hi) && (d.gain == TSL2561Gain16X) {
   271  				// drop gain and try again
   272  				err = d.SetGain(TSL2561Gain1X)
   273  				if err != nil {
   274  					return
   275  				}
   276  				agcCheck = true
   277  			} else {
   278  				// Reading is either valid, or we're already at the chips
   279  				// limits
   280  				valid = true
   281  			}
   282  		} else {
   283  			// If we've already adjusted the gain once, just return the new results.
   284  			// This avoids endless loops where a value is at one extreme pre-gain,
   285  			// and the the other extreme post-gain
   286  			valid = true
   287  		}
   288  
   289  		if valid {
   290  			break
   291  		}
   292  	}
   293  
   294  	return
   295  }
   296  
   297  // CalculateLux converts raw sensor values to the standard SI Lux equivalent.
   298  // Returns 65536 if the sensor is saturated.
   299  func (d *TSL2561Driver) CalculateLux(broadband uint16, ir uint16) (lux uint32) {
   300  	var channel1 uint32
   301  	var channel0 uint32
   302  
   303  	// Set cliplevel and scaling based on integration time
   304  	clipThreshold, chScale := d.getClipScaling()
   305  
   306  	// Saturated sensor
   307  	if (broadband > clipThreshold) || (ir > clipThreshold) {
   308  		return 65536
   309  	}
   310  
   311  	// Adjust scale for gain
   312  	if d.gain == TSL2561Gain1X {
   313  		chScale = chScale * 16
   314  	}
   315  
   316  	channel0 = (uint32(broadband) * chScale) >> tsl2561LuxChScale
   317  	channel1 = (uint32(ir) * chScale) >> tsl2561LuxChScale
   318  
   319  	// Find the ratio of the channel values (channel1 / channel0)
   320  	var ratio1 uint32
   321  	if channel0 != 0 {
   322  		ratio1 = (channel1 << (tsl2561LuxRatioScale + 1)) / channel0
   323  	}
   324  
   325  	// Round the ratio value
   326  	ratio := (ratio1 + 1) / 2
   327  
   328  	b, m := d.getBM(ratio)
   329  	var temp uint32
   330  	if (channel0 * b) > (channel1 * m) {
   331  		temp = (channel0 * b) - (channel1 * m)
   332  	}
   333  
   334  	// Round lsb (2^(LUX_SCALE+1))
   335  	temp += (1 << (tsl2561LuxLuxScale - 1))
   336  
   337  	// Strip off fractional portion
   338  	lux = temp >> tsl2561LuxLuxScale
   339  
   340  	return lux
   341  }
   342  
   343  func (d *TSL2561Driver) enable() (err error) {
   344  	err = d.connection.WriteByteData(uint8(tsl2561CommandBit|tsl2561RegisterControl), tsl2561ControlPowerOn)
   345  	return err
   346  }
   347  
   348  func (d *TSL2561Driver) disable() (err error) {
   349  	err = d.connection.WriteByteData(uint8(tsl2561CommandBit|tsl2561RegisterControl), tsl2561ControlPowerOff)
   350  	return err
   351  }
   352  
   353  func (d *TSL2561Driver) getData() (broadband uint16, ir uint16, err error) {
   354  	if err = d.enable(); err != nil {
   355  		return
   356  	}
   357  
   358  	d.waitForADC()
   359  
   360  	// Reads a two byte value from channel 0 (visible + infrared)
   361  	broadband, err = d.connection.ReadWordData(tsl2561CommandBit | tsl2561WordBit | tsl2561RegisterChan0Low)
   362  	if err != nil {
   363  		return
   364  	}
   365  
   366  	// Reads a two byte value from channel 1 (infrared)
   367  	ir, err = d.connection.ReadWordData(tsl2561CommandBit | tsl2561WordBit | tsl2561RegisterChan1Low)
   368  	if err != nil {
   369  		return
   370  	}
   371  
   372  	err = d.disable()
   373  
   374  	return
   375  }
   376  
   377  func (d *TSL2561Driver) getHiLo() (hi, lo uint16) {
   378  	switch d.integrationTime {
   379  	case TSL2561IntegrationTime13MS:
   380  		hi = tsl2561AgcTHi13MS
   381  		lo = tsl2561AgcTLo13MS
   382  	case TSL2561IntegrationTime101MS:
   383  		hi = tsl2561AgcTHi101MS
   384  		lo = tsl2561AgcTLo101MS
   385  	case TSL2561IntegrationTime402MS:
   386  		hi = tsl2561AgcTHi402MS
   387  		lo = tsl2561AgcTLo402MS
   388  	}
   389  	return
   390  }
   391  
   392  func (d *TSL2561Driver) getClipScaling() (clipThreshold uint16, chScale uint32) {
   393  	switch d.integrationTime {
   394  	case TSL2561IntegrationTime13MS:
   395  		clipThreshold = tsl2561Clipping13MS
   396  		chScale = tsl2561LuxCHScaleTInt0
   397  	case TSL2561IntegrationTime101MS:
   398  		clipThreshold = tsl2561Clipping101MS
   399  		chScale = tsl2561LuxChScaleTInt1
   400  	case TSL2561IntegrationTime402MS:
   401  		clipThreshold = tsl2561Clipping402MS
   402  		chScale = (1 << tsl2561LuxChScale)
   403  	}
   404  	return
   405  }
   406  
   407  func (d *TSL2561Driver) getBM(ratio uint32) (b uint32, m uint32) {
   408  	switch {
   409  	case ratio <= tsl2561LuxK1T:
   410  		b = tsl2561LuxB1T
   411  		m = tsl2561LuxM1T
   412  	case (ratio <= tsl2561LuxK2T):
   413  		b = tsl2561LuxB2T
   414  		m = tsl2561LuxM2T
   415  	case (ratio <= tsl2561LuxK3T):
   416  		b = tsl2561LuxB3T
   417  		m = tsl2561LuxM3T
   418  	case (ratio <= tsl2561LuxK4T):
   419  		b = tsl2561LuxB4T
   420  		m = tsl2561LuxM4T
   421  	case (ratio <= tsl2561LuxK5T):
   422  		b = tsl2561LuxB5T
   423  		m = tsl2561LuxM5T
   424  	case (ratio <= tsl2561LuxK6T):
   425  		b = tsl2561LuxB6T
   426  		m = tsl2561LuxM6T
   427  	case (ratio <= tsl2561LuxK7T):
   428  		b = tsl2561LuxB7T
   429  		m = tsl2561LuxM7T
   430  	case (ratio > tsl2561LuxK8T): // TODO: there is a gap here...
   431  		b = tsl2561LuxB8T
   432  		m = tsl2561LuxM8T
   433  	}
   434  	return
   435  }
   436  
   437  func (d *TSL2561Driver) waitForADC() {
   438  	switch d.integrationTime {
   439  	case TSL2561IntegrationTime13MS:
   440  		time.Sleep(15 * time.Millisecond)
   441  	case TSL2561IntegrationTime101MS:
   442  		time.Sleep(120 * time.Millisecond)
   443  	case TSL2561IntegrationTime402MS:
   444  		time.Sleep(450 * time.Millisecond)
   445  	}
   446  }
   447  
   448  func (d *TSL2561Driver) initialize() error {
   449  	if err := d.enable(); err != nil {
   450  		return err
   451  	}
   452  
   453  	if initialized, err := d.connection.ReadByteData(tsl2561RegisterID); err != nil {
   454  		return err
   455  	} else if (initialized & 0x0A) == 0 {
   456  		return fmt.Errorf("TSL2561 device not found (0x%X)", initialized)
   457  	}
   458  
   459  	if err := d.SetIntegrationTime(d.integrationTime); err != nil {
   460  		return err
   461  	}
   462  
   463  	if err := d.SetGain(d.gain); err != nil {
   464  		return err
   465  	}
   466  
   467  	if err := d.disable(); err != nil {
   468  		return err
   469  	}
   470  
   471  	return nil
   472  }