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 }