TSL2561 and Raspberry Pi

General project help for Adafruit customers

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
User avatar
adafruit_support_rick
 
Posts: 35092
Joined: Tue Mar 15, 2011 11:42 am

Re: TSL2561 and Raspberry Pi

Post by adafruit_support_rick »

pandring wrote:Now, I just need to get the values into a usable format (i.e. lux). Correct me if I'm wrong, but I assume I should just use the algorithm found on page 22 of the data sheet.
That should work. But you should also refer to the adafruit library as a sanity check

pandring
 
Posts: 25
Joined: Sun Dec 02, 2012 5:15 pm

Re: TSL2561 and Raspberry Pi

Post by pandring »

Here's my code:

Code: Select all

from Adafruit_I2C import Adafruit_I2C

address = 0x39
i2c = Adafruit_I2C(address)
control_on = 0x03
control_off = 0x00

def enable():
	print "enabling"
	i2c.write8(0x80, control_on)
	
def disable():
	print "disabling"
	i2c.write8(0x80, control_off)
	
def getLight():
	x = i2c.readU16(0xAE);
  	x <<= 16;
 	x |= i2c.readU16(0xAC);
	return x
	
enable()
print "Light:"
print getLight()

pandring
 
Posts: 25
Joined: Sun Dec 02, 2012 5:15 pm

Re: TSL2561 and Raspberry Pi

Post by pandring »

@Driverblock:
So is the TSL2561 sold by adafruit the "T" or the "CS" version? I'm not sure if I have the TMB or chipscale version.

Also, you said I should combine both 16 bit values into a 32 bit value, but it looks like you need the separate values to be able to calculate lux. How do I get the channel 0 and channel 1 values if I need to combine them into one 32 bit string?

User avatar
static
 
Posts: 188
Joined: Thu Dec 23, 2010 6:21 pm

Re: TSL2561 and Raspberry Pi

Post by static »

Looking at the first page, I think we're looking at the T-Package.

I'm playing around with your code, but I'm really confused by the getLight() function. As far as I know, a semi-colon is used to put several lines of code on a single line. Are they necessary here?

I rewrote the code a little bit so that each value stored before being re-checked or altered by the code.

Code: Select all

def getLight():
    x = i2c.readU16(0xAE)
    w = x
    x <<= 16
    y=x
    x|=i2c.readU16(0xAC)
    return w,x,y
I'm looking at page 17 and 18 on the datasheet. It mentions bit-shifting and shadow registers. Does this mean that it matters which register we read first? Should we be looking at 0xAC first and then OxAE? Experimenting with the code and subsequent data readouts, I am getting significantly different values when I switch which order I'm pulling the data.

User avatar
adafruit_support_rick
 
Posts: 35092
Joined: Tue Mar 15, 2011 11:42 am

Re: TSL2561 and Raspberry Pi

Post by adafruit_support_rick »

pandring wrote:So is the TSL2561 sold by adafruit the "T" or the "CS" version?
The T version
pandring wrote:Also, you said I should combine both 16 bit values into a 32 bit value, but it looks like you need the separate values to be able to calculate lux. How do I get the channel 0 and channel 1 values if I need to combine them into one 32 bit string?
I didn't say you had to do that. I was just illustrating what the arduino library function did. To get the individual values, just assign the read16 results from channel 0 and channel 1 to different variables.

User avatar
static
 
Posts: 188
Joined: Thu Dec 23, 2010 6:21 pm

Re: TSL2561 and Raspberry Pi

Post by static »

Why do we need the bit shift, if we're pulling the values separately?

Code: Select all

def getLight2():
    x = i2c.readU16(0xAC)
    y = i2c.readU16(0xAE)
    return x,y

User avatar
adafruit_support_rick
 
Posts: 35092
Joined: Tue Mar 15, 2011 11:42 am

Re: TSL2561 and Raspberry Pi

Post by adafruit_support_rick »

You don't. My approach to this problem would be to simply port the Adafruit arduino library. The C++ code will map pretty easily into python code. I used the getFullLuminosity() function as an example. It reads both registers and packs them into a uint32_t for the return value. The caller then unpacks that back into individual 16-bit values.

C++ doesn't allow two return values, hence the uint32_t. If you can return two int16's from a python function, then there's no need for the shift.

While building this library by referring only the datasheet may be an interesting exercise, you will cut your BANNED expenses significantly by simply porting the C++. It doesn't amount to much more than juggling a few keywords and punctuation symbols.

I'm also see a lot of literals like readU16(0xAE). Very bad. You want to use constants the way the arduino library does. In my example, I reduced the constants down to literals as an illustration.

User avatar
static
 
Posts: 188
Joined: Thu Dec 23, 2010 6:21 pm

Re: TSL2561 and Raspberry Pi

Post by static »

Code: Select all

from Adafruit_I2C import Adafruit_I2C
import time
import math
address = 0x39
i2c = Adafruit_I2C(address)
control_on = 0x03
control_off = 0x00



def enable():
    print("enabling")
    i2c.write8(0x80, control_on)

def disable():
    print("disabling")
    i2c.write8(0x80, control_off)

def getLight():
    ch0 = i2c.readU16(0xAC) ##Broad spectrum photo-diode
    ch1 = i2c.readU16(0xAE) ##IR spectrum photo-diode
    return ch0,ch1

def getLux():
    ch0,ch1 = getLight()
    ratio = 0
    lux = 0
    tag = 0
    if ch0 > 0:
        ratio = float(ch1)/float(ch0)
    if ch0 == 0:
        ratio = 1.35

    if ratio > 1.30:
        lux = 0
        tag = 0
    elif ratio > 0.80:
        lux = (0.00146 * ch0) - (0.00112 * ch1)
        tag = 4
    elif ratio > 0.61:
        lux = (0.0128 * ch0) - (0.0153 * ch1)
        tag =3
    elif ratio > 0.50:
        lux = (0.0224 * ch0) - (0.031 * ch1)
        tag = 2
    elif ratio <= 0.50:
        lux = (0.0304 * ch0) - (0.062 * ch0 * ((ch1/ch0) ** 1.4))
        tag =1
    return lux, ratio, tag, ch0, ch1

enable()
#time.sleep(0.4)
light = getLight()
lux = getLux()
print(light)
print(lux)
#disable()
getLux() returns lux, ratio, a debugging tag, and the channel0 and channel1 raw values. I'm getting some bizarre readings at times. If I shine my lights directly on the sensor, occasionally I get readings of 0 lux. One of the lights is a SureFire, a pretty bright light.

I need to clean the code up, and implement DriverBlock's suggestion of implementing constants. I'm pretty sure the if/elif loop describes the function in the datasheet.

I wrote this in Python 3. I think the only thing that needs to be changed for Python 2.x functionality is to switch the print() statements to Python 2.x print statements.

User avatar
static
 
Posts: 188
Joined: Thu Dec 23, 2010 6:21 pm

Re: TSL2561 and Raspberry Pi

Post by static »

Python 3 code:

Code: Select all

from Adafruit_I2C import Adafruit_I2C
import time
import math
address = 0x39
i2c = Adafruit_I2C(address)
control_on = 0x03
control_off = 0x00
BSPD = 0xAC                 ####  Broad Spectrum Photo-Diode (Visible and IR)
IRPD = 0xAE                 ####  IR spectrum Photo-Diode



def enable():
    print("enabling")
    i2c.write8(0x80, control_on)

def disable():
    print("disabling")
    i2c.write8(0x80, control_off)

def getLight():
    ch0 = i2c.readU16(BSPD) ##Broad spectrum photo-diode
    ch1 = i2c.readU16(IRPD) ##IR spectrum photo-diode
    return ch0,ch1

def getLux():
    """This code appears to give wonky results at times (wonky being a highly
    technical term).  When the light sensing apparatus is struck by a relatively
    intense light, the results are unpredictable"""
    ch0,ch1 = getLight()
    ratio = 0
    lux = 0
    tag = 0
    if ch0 > 0:
        ratio = float(ch1)/float(ch0)
    #if ch0 == 0:
        #ratio = 1.35

    if ratio > 1.30:
        lux = 0
        tag = 0
    elif ratio > 0.80:
        lux = (0.00146 * ch0) - (0.00112 * ch1)
        tag = 4
    elif ratio > 0.61:
        lux = (0.0128 * ch0) - (0.0153 * ch1)
        tag =3
    elif ratio > 0.50:
        lux = (0.0224 * ch0) - (0.031 * ch1)
        tag = 2
    elif ratio <= 0.50:
        lux = (0.0304 * ch0) - (0.062 * ch0 * ((ch1/ch0) ** 1.4))
        tag =1
    return lux

def getLuxData():
    ch0,ch1 = getLight()
    ratio = 0
    lux = 0
    tag = 0
    if ch0 > 0:
        ratio = float(ch1)/float(ch0)
    #if ch0 == 0:
        #ratio = 1.35

    if ratio > 1.30:
        lux = 0
        tag = 0
    elif ratio > 0.80:
        lux = (0.00146 * ch0) - (0.00112 * ch1)
        tag = 4
    elif ratio > 0.61:
        lux = (0.0128 * ch0) - (0.0153 * ch1)
        tag =3
    elif ratio > 0.50:
        lux = (0.0224 * ch0) - (0.031 * ch1)
        tag = 2
    elif ratio <= 0.50:
        lux = (0.0304 * ch0) - (0.062 * ch0 * ((ch1/ch0) ** 1.4))
        tag =1
    return lux, tag, ratio, ch0, ch1

enable()

lux = getLux()
print(lux)

#while 1 > 0:
#    lux, tag, ratio, ch0, ch1 = getLuxData()
#    if lux > 10:
#        lux = int(lux)
#    ratio = round(ratio, 4)
#    print(lux, ratio)
    
#    time.sleep(0.5)

    
        
        
        
        

Hopefully, Python 2.x code:

Code: Select all

from Adafruit_I2C import Adafruit_I2C
import time
import math
address = 0x39
i2c = Adafruit_I2C(address)
control_on = 0x03
control_off = 0x00
BSPD = 0xAC                 ####  Broad Spectrum Photo-Diode (Visible and IR)
IRPD = 0xAE                 ####  IR spectrum Photo-Diode



def enable():
    print"enabling"
    i2c.write8(0x80, control_on)

def disable():
    print"disabling"
    i2c.write8(0x80, control_off)

def getLight():
    ch0 = i2c.readU16(BSPD) ##Broad spectrum photo-diode
    ch1 = i2c.readU16(IRPD) ##IR spectrum photo-diode
    return ch0,ch1

def getLux():
    """This code appears to give wonky results at times (wonky being a highly
    technical term).  When the light sensing apparatus is struck by a relatively
    intense light, the results are unpredictable"""
    ch0,ch1 = getLight()
    ratio = 0
    lux = 0
    tag = 0
    if ch0 > 0:
        ratio = float(ch1)/float(ch0)
    #if ch0 == 0:
        #ratio = 1.35

    if ratio > 1.30:
        lux = 0
        tag = 0
    elif ratio > 0.80:
        lux = (0.00146 * ch0) - (0.00112 * ch1)
        tag = 4
    elif ratio > 0.61:
        lux = (0.0128 * ch0) - (0.0153 * ch1)
        tag =3
    elif ratio > 0.50:
        lux = (0.0224 * ch0) - (0.031 * ch1)
        tag = 2
    elif ratio <= 0.50:
        lux = (0.0304 * ch0) - (0.062 * ch0 * ((ch1/ch0) ** 1.4))
        tag =1
    return lux

def getLuxData():
    ch0,ch1 = getLight()
    ratio = 0
    lux = 0
    tag = 0
    if ch0 > 0:
        ratio = float(ch1)/float(ch0)
    #if ch0 == 0:
        #ratio = 1.35

    if ratio > 1.30:
        lux = 0
        tag = 0
    elif ratio > 0.80:
        lux = (0.00146 * ch0) - (0.00112 * ch1)
        tag = 4
    elif ratio > 0.61:
        lux = (0.0128 * ch0) - (0.0153 * ch1)
        tag =3
    elif ratio > 0.50:
        lux = (0.0224 * ch0) - (0.031 * ch1)
        tag = 2
    elif ratio <= 0.50:
        lux = (0.0304 * ch0) - (0.062 * ch0 * ((ch1/ch0) ** 1.4))
        tag =1
    return lux, tag, ratio, ch0, ch1

enable()

lux = getLux()
print lux

#while 1 > 0:
#    lux, tag, ratio, ch0, ch1 = getLuxData()
#    if lux > 10:
#        lux = int(lux)
#    ratio = round(ratio, 4)
#    print lux, ratio
    
#    time.sleep(0.5)
 
I still don't understand the bizarre behavior when I put a ton of light on the sensor. I tried a couple of different methods to see what was happening with the lux mathematical function, but I couldn't really piece it together (lack of sleep?).

User avatar
adafruit_support_rick
 
Posts: 35092
Joined: Tue Mar 15, 2011 11:42 am

Re: TSL2561 and Raspberry Pi

Post by adafruit_support_rick »


User avatar
static
 
Posts: 188
Joined: Thu Dec 23, 2010 6:21 pm

Re: TSL2561 and Raspberry Pi

Post by static »

All over it. Time to do some testing.
Thanks man, it really didn't occur to me to search the topic list again.

User avatar
static
 
Posts: 188
Joined: Thu Dec 23, 2010 6:21 pm

Re: TSL2561 and Raspberry Pi

Post by static »

Code: Select all

#!/usr/bin/python

import sys
import smbus
import time
from Adafruit_I2C import Adafruit_I2C

### Written for Python 3
### Big thanks to bryand, who wrote the code that I borrowed heavily from/was inspired by
### More thanks pandring who kind of kickstarted my work on the TSL2561 sensor
### A great big huge thanks to driverblock and the Adafruit team (Congrats on your many succeses
### Ladyada).  Without you folks I would just be a guy sitting somewhere thinking about cool stuff
### Now I'm a guy building cool stuff.
### If any of this code proves useful, drop me a line at medicforlife.blogspot.com 


class Luxmeter:
    i2c = None

    def __init__(self, address=0x39, debug=0, pause=0.41):
        self.i2c = Adafruit_I2C(address)
        self.address = address
        self.pause = pause
        self.debug = debug

        self.i2c.write8(0x80, 0x03)     # enable the device
        self.i2c.write8(0x81, 0x11)     # set gain = 16X and timing = 101 mSec
        time.sleep(self.pause)          # pause for a warm-up

    def readfull(self, reg=0x8C):
        """Reads visible + IR diode from the I2C device"""
        try:
            fullval = self.i2c.readU16(reg)
            newval = self.i2c.reverseByteOrder(fullval)
            if (self.debug):
                print("I2C: Device 0x%02X returned 0x%04X from reg 0x%02X" % (self.address, fullval & 0xFFFF, reg))
            return newval
        except IOError:
            print("Error accessing 0x%02X: Check your I2C address" % self.address)
            return -1

    def readIR(self, reg=0x8E):
        """Reads IR only diode from the I2C device"""
        try:
            IRval = self.i2c.readU16(reg)
            newIR = self.i2c.reverseByteOrder(IRval)
            if (self.debug):
                print("I2C: Device 0x%02X returned 0x%04X from reg 0x%02X" % (self.address, IRval & 0xFFFF, reg))
            return newIR
        except IOError:
            print("Error accessing 0x%02X: Check your I2C address" % self.address)
            return -1

    def readfullauto(self, reg=0x8c):
        """Reads visible + IR diode from the I2C device with auto ranging"""
        try:
            fullval = self.i2c.readU16(reg)
            newval = self.i2c.reverseByteOrder(fullval)
            if newval >= 37177:
                self.i2c.write8(0x81, 0x01)
                time.sleep(self.pause)
                fullval = self.i2c.readU16(reg)
                newval = self.i2c.reverseByteOrder(fullval)
            if (self.debug):
                print("I2C: Device 0x%02X returned 0x%04X from reg 0x%02X" % (self.address, fullval & 0xFFFF, reg))
            return newval
        except IOError:
            print("Error accessing 0x%02X: Check your I2C address" % self.address)
            return -1

    def readIRauto(self, reg=0x8e):
        """Reads IR diode from the I2C device with auto ranging"""
        try:
            IRval = self.i2c.readU16(reg)
            newIR = self.i2c.reverseByteOrder(IRval)
            if newIR >= 37177:
                self.i2c.write8(0x81, 0x01)     #   remove 16x gain
                time.sleep(self.pause)
                IRval = self.i2c.readU16(reg)
                newIR = self.i2c.reverseByteOrder(IRval)
            if (self.debug):
                print("I2C: Device 0x%02X returned 0x%04X from reg 0x%02X" % (self.address, IRval & 0xFFFF, reg))
            return newIR
        except IOError:
            print("Error accessing 0x%02X: Check your I2C address" % self.address)
            return -1

def luxread(address = 0x39, debug = False, autorange = True):
    """Grabs a lux reading either with autoranging or without"""
    LuxSensor = Luxmeter(0x39, False)
    if autorange == True:
        ambient = LuxSensor.readfullauto()
        IR = LuxSensor.readIRauto()
    else:
        ambient = LuxSensor.readfull()
        IR = LuxSensor.readIR()
        
    ratio = (float) (IR / ambient)

    if ((ratio >= 0) & (ratio <= 0.52)):
        lux = (0.0315 * ambient) - (0.0593 * ambient * (ratio**1.4))
    elif (ratio <= 0.65):
        lux = (0.0229 * ambient) - (0.0291 * IR)
    elif (ratio <= 0.80):
        lux = (0.0157 * ambient) - (0.018 + IR)
    elif (ratio <= 1.3):
        lux = (0.00338 * ambient) - (0.0026 * IR)
    elif (ratio > 1.3):
        lux = 0
    
    return lux


print(luxread())
I've been getting good results from this code in the office. With the additions of the delays, I don't seem to be getting any 0 results when I hit the sensor with a strong light.
I just mounted the sensor in my external sensor pod. I'm going to see how it performs over a couple of days.
Let me know if you have any suggestions.

Thanks,
Static

scortier
 
Posts: 13
Joined: Mon Jan 14, 2013 2:47 pm

Re: TSL2561 and Raspberry Pi

Post by scortier »

Static: I am wondering why your math changed from the first code you wrote for the sensor and the most recent one.

User avatar
static
 
Posts: 188
Joined: Thu Dec 23, 2010 6:21 pm

Re: TSL2561 and Raspberry Pi

Post by static »

Which math?
The code I pulled really was from pandring's code (earlier in the thread you posted in), but was largely taken from bryand's code in this thread:
http://forums.adafruit.com/viewtopic.ph ... 33#p179633

I did a couple of tweaks, but it's mostly theirs.

scortier
 
Posts: 13
Joined: Mon Jan 14, 2013 2:47 pm

Re: TSL2561 and Raspberry Pi

Post by scortier »

Sorry, Static. I didn't realize that was bryand's code you had posted in your last comment. I was just comparing your getLux function to his luxread function with the if/else statements and ratios, but it's a trivial question now.

I tried your code with our application of saving values into a .csv file and it worked, but ran into the same problem of getting 0s when the light was too bright. I will try bryand's code myself now - fingers crossed.

Locked
Please be positive and constructive with your questions and comments.

Return to “General Project help”