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 }