gobot.io/x/gobot@v1.16.0/drivers/i2c/tsl2561_driver.go (about)

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