0

TSL2561 and Raspberry Pi
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

Re: TSL2561 and Raspberry Pi

by huelke on Thu May 02, 2013 2:46 pm

Hi there,
first of all thank you for this thread.
It helped me a lot getting started with my Pi and the TSL2561.

After I had several day-logs with a pattern Static referred to as "vampire fang" ;) I tried to solve the problem.
I don't know if you have fixed it already yourself - here is my approach:

I knew it had to be something with the lux calculation but after having logged the IR and ambient output which also made the curious "rapid drops" - it had to be the sensor itself.
So I read the datasheet again and again and I think I've found the problem:

First I understand that the calculation that is used only works if the Integration time is 402mSec - otherwise the result has to be scaled.
As Static's sourcecode uses 101 mSec integration this would result in wrong lux values - so I switched to 402 mSec ( 0x12 #high gain 402mSec OR 0x02 #low gain 402 mSec ).
However, it would not result in "vampire fangs".

Second I learned that the caluculation does only work if the gain is 16x - again otherwise it has to be scaled.
And I think thats the source of the vampire fangs: The moment the autorange function switches to 1x gain - the read IR/ambient values drop (rapidly) - and so does the resulting lux value...
So I guess the read*auto functions should multiply the read words with 16 (scaling) if it is using lowGain.
That way the values look much better.

I do not know if I am right - correct me elsewise. :wink:

FYI: The corresponding code in the datasheet is:
// first, scale the channel values depending on the gain and integration time
// 16X, 402mS is nominal. <--- !!!!!!

// scale if integration time is NOT 402 msec <--- !!!!!!
switch (tInt)
{
case 0: // 13.7 msec
chScale = CHSCALE_TINT0;
break;
case 1: // 101 msec
chScale = CHSCALE_TINT1;
break;
default: // assume no scaling
chScale = (1 << CH_SCALE);
}

// scale if gain is NOT 16X <--- !!!!!!
if (!iGain) chScale = chScale << 4; // scale 1X to 16X [huelke: shift 4 bits left=> multiply with 2**4 = 16]

// scale the channel values
channel0 = (ch0 * chScale) >> CH_SCALE;
channel1 = (ch1 * chScale) >> CH_SCALE;




I chose to deactivate autorange and ran a whole day on lowGain which produced a pretty pattern.
If you use autogain, you have to insure that you wait long enough after switching gain!
I almost got crazy because the lowGain * 16 values did not match the highGain values.
Problem was that I aquired the values too soon after switching the gain so the sensor could not complete its integration and returned wrong values.

Now - with enough "sleep" - lowGain and highGain return nearly the same lux calculation. :mrgreen:


Greets, Hueke
Last edited by huelke on Thu May 02, 2013 5:26 pm, edited 1 time in total.
huelke
 
Posts: 2
Joined: Thu May 02, 2013 11:57 am

Re: TSL2561 and Raspberry Pi

by static on Thu May 02, 2013 3:24 pm

I felt the issue had to be the autoranging, but I couldn't delve deep enough into it to tackle it. Then I disassembled things to work on the ADXL345 library.
I'll put my rig back together and try this out. Thanks, huelke!

static
 
Posts: 182
Joined: Thu Dec 23, 2010 6:21 pm

Re: TSL2561 and Raspberry Pi

by huelke on Thu May 02, 2013 4:31 pm

Hi Static,
perhaps it helps if i post my* actual code. (* of course it's your code that I just changed a bit)
Don't know if it totaly works as I just rewrote the autorange part while it is dark outside ;)

Code: Select all | TOGGLE FULL SIZE
#!/usr/bin/python

import sys
import smbus
import time
from Adafruit_I2C import Adafruit_I2C

### Written for Python 2 <-!!!
### 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.8):
        self.i2c = Adafruit_I2C(address)
        self.address = address
        self.pause = pause
        self.debug = debug
        self.gain = 0 # no gain preselected
        self.i2c.write8(0x80, 0x03)     # enable the device


    def setGain(self,gain=1):
        """ Set the gain """
        if (gain != self.gain):
            if (gain==1):
                self.i2c.write8(0x81, 0x02)     # set gain = 1X and timing = 402 mSec
                if (self.debug):
                    print "Setting low gain"
            else:
                self.i2c.write8(0x81, 0x12)     # set gain = 16X and timing = 402 mSec
                if (self.debug):
                    print "Setting high gain"
            self.gain=gain;                     # safe gain for calculation
            time.sleep(self.pause)              # pause for integration (self.pause must be bigger than integration time)


    def readWord(self, reg):
        """Reads a word from the I2C device"""
        try:
            wordval = self.i2c.readU16(reg)
            newval = self.i2c.reverseByteOrder(wordval)
            if (self.debug):
                print("I2C: Device 0x%02X returned 0x%04X from reg 0x%02X" % (self.address, wordval & 0xFFFF, reg))
            return newval
        except IOError:
            print("Error accessing 0x%02X: Check your I2C address" % self.address)
            return -1


    def readFull(self, reg=0x8C):
        """Reads visible+IR diode from the I2C device"""
        return self.readWord(reg);

    def readIR(self, reg=0x8E):
        """Reads IR only diode from the I2C device"""
        return self.readWord(reg);

    def getLux(self, gain = 0):
        """Grabs a lux reading either with autoranging (gain=0) or with a specified gain (1, 16)"""
        if (gain == 1 or gain == 16):
            self.setGain(gain) # low/highGain
            ambient = self.readFull()
            IR = self.readIR()
        elif (gain==0): # auto gain
            self.setGain(16) # first try highGain
            ambient = self.readFull()
            if (ambient < 65535):
                IR = self.readIR()
            if (ambient >= 65535 or IR >= 65535): # value(s) exeed(s) datarange
                self.setGain(1) # set lowGain
                ambient = self.readFull()
                IR = self.readIR()

        if (self.gain==1):
           ambient *= 16    # scale 1x to 16x
           IR *= 16         # scale 1x to 16x
                       
        ratio = (IR / float(ambient)) # changed to make it run under python 2

        if (self.debug):
            print "IR Result", IR
            print "Ambient Result", 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


oLuxmeter=Luxmeter()

print "LUX HIGH GAIN ", oLuxmeter.getLux(16)
print "LUX LOW GAIN ", oLuxmeter.getLux(1)
print "LUX AUTO GAIN ", oLuxmeter.getLux()
huelke
 
Posts: 2
Joined: Thu May 02, 2013 11:57 am

Re: TSL2561 and Raspberry Pi

by lordjagged28 on Sat May 18, 2013 4:55 am

Thank you for the code, I've typed it in and it works. I needed it as I am very new to all of this.
Questions are:
What are the differences between the High, Low and Auto gain? I shine a light at the sensor at it seems the high gain goes down from when the light isn't shining at it.
What is the actual number you get out?
lordjagged28
 
Posts: 5
Joined: Wed May 08, 2013 7:20 am

Re: TSL2561 and Raspberry Pi

by csalty on Thu Sep 19, 2013 1:15 am

Hello huelke. I'm a bit late to the game here but your code is really nice. Thanks so much for sharing. The *16 factor is an excellent improvement to the TSL2561 script, which is working great on my BeagleBoneBlack. The one thing I'll throw out there is that if you return the IR and ambient values you will need to divide those values by 16 if the gain is set back to 1X. Maybe I'm wrong, but I added this correction factor to my code since I'm using the sensor mainly for the IR detector. Here is the "patch" that I'm using in place of where you have "return lux".

Thanks again!

CSALTY

Code: Select all | TOGGLE FULL SIZE
.
.
.

        if (self.gain==1):
            return (ambient/16, IR/16, lux)
        else:
            return (ambient, IR, lux)


if __name__ == "__main__":

    myGain = (16, 1, 0)
   
    while(True):
        for each in myGain:
            myAmbient, myIR, myLux = Luxmeter().getLux(each)
            print "%d GAIN: LUX:%d IR:%d Ambient:%d" % (each, myLux, myIR, myAmbient)
csalty
 
Posts: 27
Joined: Sat Feb 23, 2013 8:32 pm

Re: TSL2561 and Raspberry Pi

by dukeofdoom on Wed Oct 02, 2013 10:10 pm

hulke - great code thanks! Works well for my needs.
dukeofdoom
 
Posts: 15
Joined: Tue Feb 12, 2013 9:26 pm

Re: TSL2561 and Raspberry Pi

by schwabbbel on Wed Oct 09, 2013 2:25 pm

Hi there,
Thank you all for this series of posts. I was reading it back and forth the whole day.
I tested the script Huelke posted. It did run but I am not content with the values it returns.
I can compare the values from the RPi & TSL2561 with an arduino & TSL2561 and with an hand-held luxmeter (MS6610). The arduino and the luxmeter differ by about 100 Lux. And the result from the python script is nowhere near and also varies a lot. Therefore I went through the code, which led to a few questions:
1. Shouldn't there be a "and" instead of an "&" in the getLux function:
Code: Select all | TOGGLE FULL SIZE
        if ((ratio >= 0) & (ratio <= 0.52)): #<-Here and instead of &
            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


2. The values in the same code section as above are for the Chipscale Package. I thought that we are using the TMB package. My suggestion:
Code: Select all | TOGGLE FULL SIZE
        if ((ratio >= 0) and (ratio <= 0.50)):
            lux = (0.0304 * ambient) - (0.062 * ambient * (ratio**1.4))
        elif (ratio <= 0.61):
            lux = (0.0224 * ambient) - (0.031 * IR)
        elif (ratio <= 0.80):
            lux = (0.0128 * ambient) - (0.0153 * IR)
        elif (ratio <= 1.3):
            lux = (0.00146 * ambient) - (0.00112 * IR)
        elif (ratio > 1.3):
            lux = 0


3. Do we need the (ratio >= 0) condition in the same passage? (Since we are dealing with positive numbers only) Or should there also be this condition in the following elifs? My suggestion:
Code: Select all | TOGGLE FULL SIZE
        if (ratio <= 0.50):
            lux = (0.0304 * ambient) - (0.062 * ambient * (ratio**1.4))
        elif (ratio <= 0.61):
            lux = (0.0224 * ambient) - (0.031 * IR)
        elif (ratio <= 0.80):
            lux = (0.0128 * ambient) - (0.0153 * IR)
        elif (ratio <= 1.3):
            lux = (0.00146 * ambient) - (0.00112 * IR)
        elif (ratio > 1.3):
            lux = 0

But that renders the first question redundant.

Even if I apply these small corrections, the output remains with the same characteristics and is far from similar to the output of the arduino or the luxmeter.

I'm a total newbie with Python and RPi. I am so new to this, that I learned python with this script from huelke. So there is a chance that the whole post is just c***. In this case I apologize.
I'll try to get it fixed later this week.
Anyway, thank you for the preceding posts.

Mario

schwabbbel
 
Posts: 10
Joined: Wed Oct 09, 2013 8:34 am

Re: TSL2561 and Raspberry Pi

by static on Wed Oct 09, 2013 2:38 pm

No apologies needed!
You are actually bringing exactly what we need to "the fight". Someone with multiple methods of measuring a light source.

When I originally wrote the code I had significant problems. There were times where I was getting negative numbers (coding error, not hardware error), and I wanted to explicitly catch them when this was happening. I think the condition (ratio >=0) is a holdover from that.
Next, I think Python treats "&" and "and" as functional equivalents. I think. I'm trying to get familiar with a couple of different languages right now, so I may be wrong.

I honestly don't know which package I'm using. I thought it was Chipscale, but that could be the only one that I found notes for.

I'm working with my Pi's right now, as I find I need some security cameras. I'll try to get them wired up to get back to work on the TSL2561 project (along with the other sensors).

static
 
Posts: 182
Joined: Thu Dec 23, 2010 6:21 pm

Re: TSL2561 and Raspberry Pi

by schwabbbel on Fri Oct 11, 2013 2:11 pm

Hi there,
In order to get a python library for the TSL2561 for the Raspberry Pi quickly, I tried a different approach:
I translated the Adafruit Arduino library from C++ to python. I was hoping to avoid to think too much about the logic, i2c-communication and all the bits and bytes.
The Arduino library is intended to work with the "Adafruit unified sensor driver". I didn't see an equivalent for the RPi. So I didn't translate that part and left it commented out.
Because you can't pass variables by reference in Python (At least I don't know how), I added the attributes _ir, _broadband. Also the attributes _debug and _i2c were added. Now instead of passing variables by reference the attributes get updated.

The script is still pretty rough. There is also some leftover C++ code. But it works, at least for me:
Code: Select all | TOGGLE FULL SIZE
#!/usr/bin/python

# Python library for the TSL2561 digital luminosity (light) sensors.
# Version 0

# This library is heavily based on the Arduino library for the TSL2561 digital luminosity (light) sensors.
# It is basically a simple translation from C++ to Python.
# The thread on the Adafruit forum helped a lot to do this.
# Thanks to static, huelke, pandring, adafruit_support_rick, scortier, bryand, csalty, lenos and of course to Adafruit
# Source for the Arduino library: https://github.com/adafruit/TSL2561-Arduino-Library
# Adafruit form thread:http://forums.adafruit.com/viewtopic.php?f=8&t=34922&sid=8336d566f2f03c25882aaf34c8a15a92

import time
from Adafruit_I2C import Adafruit_I2C

class Adafruit_TSL2651(Adafruit_I2C):
    TSL2561_VISIBLE           =2       # channel 0 - channel 1
    TSL2561_INFRARED          =1       # channel 1
    TSL2561_FULLSPECTRUM      =0       # channel 0

    # I2C address options
    TSL2561_ADDR_LOW          =0x29
    TSL2561_ADDR_FLOAT        =0x39    # Default address (pin left floating)
    TSL2561_ADDR_HIGH         =0x49

    # Lux calculations differ slightly for CS package
    TSL2561_PACKAGE_CS        =0
    TSL2561_PACKAGE_T_FN_CL   =1

    TSL2561_COMMAND_BIT       =0x80    # Must be 1
    TSL2561_CLEAR_BIT         =0x40    # Clears any pending interrupt (write 1 to clear)
    TSL2561_WORD_BIT          =0x20    # 1 = read/write word (rather than byte)
    TSL2561_BLOCK_BIT         =0x10    # 1 = using block read/write

    TSL2561_CONTROL_POWERON   =0x03
    TSL2561_CONTROL_POWEROFF  =0x00

    TSL2561_LUX_LUXSCALE      =14      # Scale by 2^14
    TSL2561_LUX_RATIOSCALE    =9       # Scale ratio by 2^9
    TSL2561_LUX_CHSCALE       =10      # Scale channel values by 2^10
    TSL2561_LUX_CHSCALE_TINT0 =0x7517  # 322/11 * 2^TSL2561_LUX_CHSCALE
    TSL2561_LUX_CHSCALE_TINT1 =0x0FE7  # 322/81 * 2^TSL2561_LUX_CHSCALE

    # T, FN and CL package values
    TSL2561_LUX_K1T           =0x0040  # 0.125 * 2^RATIO_SCALE
    TSL2561_LUX_B1T           =0x01f2  # 0.0304 * 2^LUX_SCALE
    TSL2561_LUX_M1T           =0x01be  # 0.0272 * 2^LUX_SCALE
    TSL2561_LUX_K2T           =0x0080  # 0.250 * 2^RATIO_SCALE
    TSL2561_LUX_B2T           =0x0214  # 0.0325 * 2^LUX_SCALE
    TSL2561_LUX_M2T           =0x02d1  # 0.0440 * 2^LUX_SCALE
    TSL2561_LUX_K3T           =0x00c0  # 0.375 * 2^RATIO_SCALE
    TSL2561_LUX_B3T           =0x023f  # 0.0351 * 2^LUX_SCALE
    TSL2561_LUX_M3T           =0x037b  # 0.0544 * 2^LUX_SCALE
    TSL2561_LUX_K4T           =0x0100  # 0.50 * 2^RATIO_SCALE
    TSL2561_LUX_B4T           =0x0270  # 0.0381 * 2^LUX_SCALE
    TSL2561_LUX_M4T           =0x03fe  # 0.0624 * 2^LUX_SCALE
    TSL2561_LUX_K5T           =0x0138  # 0.61 * 2^RATIO_SCALE
    TSL2561_LUX_B5T           =0x016f  # 0.0224 * 2^LUX_SCALE
    TSL2561_LUX_M5T           =0x01fc  # 0.0310 * 2^LUX_SCALE
    TSL2561_LUX_K6T           =0x019a  # 0.80 * 2^RATIO_SCALE
    TSL2561_LUX_B6T           =0x00d2  # 0.0128 * 2^LUX_SCALE
    TSL2561_LUX_M6T           =0x00fb  # 0.0153 * 2^LUX_SCALE
    TSL2561_LUX_K7T           =0x029a  # 1.3 * 2^RATIO_SCALE
    TSL2561_LUX_B7T           =0x0018  # 0.00146 * 2^LUX_SCALE
    TSL2561_LUX_M7T           =0x0012  # 0.00112 * 2^LUX_SCALE
    TSL2561_LUX_K8T           =0x029a  # 1.3 * 2^RATIO_SCALE
    TSL2561_LUX_B8T           =0x0000  # 0.000 * 2^LUX_SCALE
    TSL2561_LUX_M8T           =0x0000  # 0.000 * 2^LUX_SCALE
   
    # CS package values
    TSL2561_LUX_K1C           =0x0043  # 0.130 * 2^RATIO_SCALE
    TSL2561_LUX_B1C           =0x0204  # 0.0315 * 2^LUX_SCALE
    TSL2561_LUX_M1C           =0x01ad  # 0.0262 * 2^LUX_SCALE
    TSL2561_LUX_K2C           =0x0085  # 0.260 * 2^RATIO_SCALE
    TSL2561_LUX_B2C           =0x0228  # 0.0337 * 2^LUX_SCALE
    TSL2561_LUX_M2C           =0x02c1  # 0.0430 * 2^LUX_SCALE
    TSL2561_LUX_K3C           =0x00c8  # 0.390 * 2^RATIO_SCALE
    TSL2561_LUX_B3C           =0x0253  # 0.0363 * 2^LUX_SCALE
    TSL2561_LUX_M3C           =0x0363  # 0.0529 * 2^LUX_SCALE
    TSL2561_LUX_K4C           =0x010a  # 0.520 * 2^RATIO_SCALE
    TSL2561_LUX_B4C           =0x0282  # 0.0392 * 2^LUX_SCALE
    TSL2561_LUX_M4C           =0x03df  # 0.0605 * 2^LUX_SCALE
    TSL2561_LUX_K5C           =0x014d  # 0.65 * 2^RATIO_SCALE
    TSL2561_LUX_B5C           =0x0177  # 0.0229 * 2^LUX_SCALE
    TSL2561_LUX_M5C           =0x01dd  # 0.0291 * 2^LUX_SCALE
    TSL2561_LUX_K6C           =0x019a  # 0.80 * 2^RATIO_SCALE
    TSL2561_LUX_B6C           =0x0101  # 0.0157 * 2^LUX_SCALE
    TSL2561_LUX_M6C           =0x0127  # 0.0180 * 2^LUX_SCALE
    TSL2561_LUX_K7C           =0x029a  # 1.3 * 2^RATIO_SCALE
    TSL2561_LUX_B7C           =0x0037  # 0.00338 * 2^LUX_SCALE
    TSL2561_LUX_M7C           =0x002b  # 0.00260 * 2^LUX_SCALE
    TSL2561_LUX_K8C           =0x029a  # 1.3 * 2^RATIO_SCALE
    TSL2561_LUX_B8C           =0x0000  # 0.000 * 2^LUX_SCALE
    TSL2561_LUX_M8C           =0x0000  # 0.000 * 2^LUX_SCALE

    # Auto-gain thresholds
    TSL2561_AGC_THI_13MS      =4850    # Max value at Ti 13ms = 5047
    TSL2561_AGC_TLO_13MS      =100
    TSL2561_AGC_THI_101MS     =36000   # Max value at Ti 101ms = 37177
    TSL2561_AGC_TLO_101MS     =200
    TSL2561_AGC_THI_402MS     =63000   # Max value at Ti 402ms = 65535
    TSL2561_AGC_TLO_402MS     =500

    # Clipping thresholds
    TSL2561_CLIPPING_13MS     =4900
    TSL2561_CLIPPING_101MS    =37000
    TSL2561_CLIPPING_402MS    =65000

    TSL2561_REGISTER_CONTROL          = 0x00
    TSL2561_REGISTER_TIMING           = 0x01
    TSL2561_REGISTER_THRESHHOLDL_LOW  = 0x02
    TSL2561_REGISTER_THRESHHOLDL_HIGH = 0x03
    TSL2561_REGISTER_THRESHHOLDH_LOW  = 0x04
    TSL2561_REGISTER_THRESHHOLDH_HIGH = 0x05
    TSL2561_REGISTER_INTERRUPT        = 0x06
    TSL2561_REGISTER_CRC              = 0x08
    TSL2561_REGISTER_ID               = 0x0A
    TSL2561_REGISTER_CHAN0_LOW        = 0x0C
    TSL2561_REGISTER_CHAN0_HIGH       = 0x0D
    TSL2561_REGISTER_CHAN1_LOW        = 0x0E
    TSL2561_REGISTER_CHAN1_HIGH       = 0x0F
   
    TSL2561_INTEGRATIONTIME_13MS      = 0x00    # 13.7ms
    TSL2561_INTEGRATIONTIME_101MS     = 0x01    # 101ms
    TSL2561_INTEGRATIONTIME_402MS     = 0x02    # 402ms
   
    TSL2561_GAIN_1X                   = 0x00    # No gain
    TSL2561_GAIN_16X                  = 0x10    # 16x gain
   
   
   

#**************************************************************************/
#    Writes a register and an 8 bit value over I2C
#**************************************************************************/
    def write8 (self, reg, value):
        if (self._debug == True): print "write8"
        self._i2c.write8(reg, value)
        if (self._debug == True): print "write8_end"

#**************************************************************************/
#    Reads an 8 bit value over I2C
#**************************************************************************/
    def read8(self, reg):
        if (self._debug == True): print "read8"
        return self._i2c.readS8(reg)
        if (self._debug == True): print "read8_end"

#**************************************************************************/
#   Reads a 16 bit values over I2C
#**************************************************************************/
    def read16(self, reg):
        if (self._debug == True): print "read16"
        return self._i2c.readS16(reg)
        if (self._debug == True): print "read16_end"

#**************************************************************************/
#    Enables the device
#**************************************************************************/
    def enable(self):
        if (self._debug == True): print "enable"
        # Enable the device by setting the control bit to 0x03 */
        self._i2c.write8(self.TSL2561_COMMAND_BIT | self.TSL2561_REGISTER_CONTROL, self.TSL2561_CONTROL_POWERON)
        if (self._debug == True): print "enable_end"

#**************************************************************************/
#   Disables the device (putting it in lower power sleep mode)
#**************************************************************************/
    def disable(self):
        if (self._debug == True): print "disable"
        # Turn the device off to save power */
        self._i2c.write8(self.TSL2561_COMMAND_BIT | self.TSL2561_REGISTER_CONTROL, self.TSL2561_CONTROL_POWEROFF)
        if (self._debug == True): print "disable_end"

#**************************************************************************/
#   Private function to read luminosity on both channels
#**************************************************************************/
    def getData (self):
        if (self._debug == True): print "getData"
        # Enable the device by setting the control bit to 0x03 */
        self.enable();

        # Wait x ms for ADC to complete */
        if self._tsl2561IntegrationTime == self.TSL2561_INTEGRATIONTIME_13MS:
            time.sleep(0.014)
        elif self._tsl2561IntegrationTime == self.TSL2561_INTEGRATIONTIME_101MS:
          time.sleep(0.102)
        else:
          time.sleep(0.403)


        # Reads a two byte value from channel 0 (visible + infrared) */
        self._broadband = self.read16(self.TSL2561_COMMAND_BIT | self.TSL2561_WORD_BIT | self.TSL2561_REGISTER_CHAN0_LOW);

        # Reads a two byte value from channel 1 (infrared) */
        self._ir = self.read16(self.TSL2561_COMMAND_BIT | self.TSL2561_WORD_BIT | self.TSL2561_REGISTER_CHAN1_LOW);

        # Turn the device off to save power */
        self.disable();
        if (self._debug == True): print "getData_end"

#**************************************************************************/
#   Constructor
#**************************************************************************/
    def __init__(self, addr=0x39, debug=False):
        self._debug = debug
        if (self._debug == True): print "__init__"
        self._addr = addr
        self._tsl2561Initialised = False
        self._tsl2561AutoGain = False
        self._tsl2561IntegrationTime = self.TSL2561_INTEGRATIONTIME_13MS
        self._tsl2561Gain = self.TSL2561_GAIN_1X
        self._i2c = Adafruit_I2C(self._addr)
        self._luminosity = 0
        self._broadband = 0
        self._ir = 0
        if (self._debug == True): print "__init___end"

#**************************************************************************/
#   Initializes I2C and configures the sensor (call this function before
#   doing anything else)
#**************************************************************************/
    def begin(self):
        if (self._debug == True): print "begin"
        # Make sure we're actually connected */
        x = self.read8(self.TSL2561_REGISTER_ID);
        if not(x & 0x0A):
            return False
        self._tsl2561Initialised = True

        # Set default integration time and gain */
        self.setIntegrationTime(self._tsl2561IntegrationTime)
        self.setGain(self._tsl2561Gain)

        # Note: by default, the device is in power down mode on bootup */
        self.disable()
        if (self._debug == True): print "begin_end"

        return True
 
#**************************************************************************/
#   Enables or disables the auto-gain settings when reading
#   data from the sensor
#**************************************************************************/
    def enableAutoGain(self, enable):
        if (self._debug == True): print "enableAutoGain"
        self._tsl2561AutoGain = enable if True else False
        if (enable == True):
            self._tsl2561AutoGain = enable
        else:
            self._tsl2561AutoGain = False
        if (self._debug == True): print "enableAutoGain_end"

#**************************************************************************/
#   Sets the integration time for the TSL2561
#**************************************************************************/
    def setIntegrationTime(self, time):
        if (self._debug == True): print "setIntegrationTime"
        if (not self._tsl2561Initialised):
            self.begin()

        # Enable the device by setting the control bit to 0x03 */
        self.enable();

        # Update the timing register */
        self.write8(self.TSL2561_COMMAND_BIT | self.TSL2561_REGISTER_TIMING, time | self._tsl2561Gain)

        # Update value placeholders */
        self._tsl2561IntegrationTime = time

        # Turn the device off to save power */
        self.disable()
        if (self._debug == True): print "setIntegrationTime_end"
 
#**************************************************************************/
#    Adjusts the gain on the TSL2561 (adjusts the sensitivity to light)
#**************************************************************************/
    def setGain(self, gain):
        if (self._debug == True): print "setGain"
        if (not self._tsl2561Initialised):
            begin()

        # Enable the device by setting the control bit to 0x03 */
        self.enable()

        # Update the timing register */
        self.write8(self.TSL2561_COMMAND_BIT | self.TSL2561_REGISTER_TIMING, self._tsl2561IntegrationTime | gain)

        # Update value placeholders */
        self._tsl2561Gain = gain

        # Turn the device off to save power */
        self.disable()
        if (self._debug == True): print "setGain_end"

#**************************************************************************/
#   Gets the broadband (mixed lighting) and IR only values from
#   the TSL2561, adjusting gain if auto-gain is enabled
#**************************************************************************/
    def getLuminosity (self):
        if (self._debug == True): print "getLuminosity"
        valid = False

        if (not self._tsl2561Initialised):
             self.begin()

        # If Auto gain disabled get a single reading and continue */
        if(not self._tsl2561AutoGain):
            self.getData()
            return

        # Read data until we find a valid range */
        _agcCheck = False
        while (not valid):
            _it = self._tsl2561IntegrationTime;

            # Get the hi/low threshold for the current integration time */
            if _it==self.TSL2561_INTEGRATIONTIME_13MS:
                _hi = self.TSL2561_AGC_THI_13MS
                _lo = self.TSL2561_AGC_TLO_13MS
            elif _it==self.TSL2561_INTEGRATIONTIME_101MS:
                _hi = self.TSL2561_AGC_THI_101MS
                _lo = self.TSL2561_AGC_TLO_101MS
            else:
                _hi = self.TSL2561_AGC_THI_402MS
                _lo = self.TSL2561_AGC_TLO_402MS

            self.getData()

            # Run an auto-gain check if we haven't already done so ... */
            if (not _agcCheck):
                if ((self._broadband < _lo) and (self._tsl2561Gain == self.TSL2561_GAIN_1X)):
                    # Increase the gain and try again */
                    self.setGain(self.TSL2561_GAIN_16X)
                    # Drop the previous conversion results */
                    self.getData()
                    # Set a flag to indicate we've adjusted the gain */
                    _agcCheck = True
                elif ((self._broadband > _hi) and (self._tsl2561Gain == self.TSL2561_GAIN_16X)):
                    # Drop gain to 1x and try again */
                    self.setGain(self.TSL2561_GAIN_1X)
                    # Drop the previous conversion results */
                    self.getData()
                    # Set a flag to indicate we've adjusted the gain */
                    _agcCheck = True
                else:
                    # Nothing to look at here, keep moving ....
                    # Reading is either valid, or we're already at the chips limits */
                    valid = True
            else:
                # If we've already adjusted the gain once, just return the new results.
                # This avoids endless loops where a value is at one extreme pre-gain,
                # and the the other extreme post-gain */
                valid = True
        if (self._debug == True): print "getLuminosity_end"
       
#**************************************************************************/
#    Converts the raw sensor values to the standard SI lux equivalent.
#    Returns 0 if the sensor is saturated and the values are unreliable.
#**************************************************************************/
    def calculateLux(self):
        if (self._debug == True): print "calculateLux"
        self.getLuminosity()
        # Make sure the sensor isn't saturated! */
        if (self._tsl2561IntegrationTime == self.TSL2561_INTEGRATIONTIME_13MS):
            clipThreshold = self.TSL2561_CLIPPING_13MS
        elif (self._tsl2561IntegrationTime == self.TSL2561_INTEGRATIONTIME_101MS):
            clipThreshold = self.TSL2561_CLIPPING_101MS
        else:
            clipThreshold = self.TSL2561_CLIPPING_402MS

        # Return 0 lux if the sensor is saturated */
        if ((self._broadband > clipThreshold) or (self._ir > clipThreshold)):
            return 0

        # Get the correct scale depending on the intergration time */
        if (self._tsl2561IntegrationTime ==self.TSL2561_INTEGRATIONTIME_13MS):
            chScale = self.TSL2561_LUX_CHSCALE_TINT0
        elif (self._tsl2561IntegrationTime ==self.TSL2561_INTEGRATIONTIME_101MS):
            chScale = self.TSL2561_LUX_CHSCALE_TINT1
        else:
            chScale = (1 << self.TSL2561_LUX_CHSCALE)

        # Scale for gain (1x or 16x) */
        if (not self._tsl2561Gain):
            chScale = chScale << 4

        # Scale the channel values */
        channel0 = (self._broadband * chScale) >> self.TSL2561_LUX_CHSCALE
        channel1 = (self._ir * chScale) >> self.TSL2561_LUX_CHSCALE

        # Find the ratio of the channel values (Channel1/Channel0) */
        ratio1 = 0;
        if (channel0 != 0):
            ratio1 = (channel1 << (self.TSL2561_LUX_RATIOSCALE+1)) / channel0

        # round the ratio value */
        ratio = (ratio1 + 1) >> 1

        if (self.TSL2561_PACKAGE_CS == 1):
            if ((ratio >= 0) and (ratio <= self.TSL2561_LUX_K1C)):
                b=self.TSL2561_LUX_B1C
                m=self.TSL2561_LUX_M1C
            elif (ratio <= self.TSL2561_LUX_K2C):
                b=self.TSL2561_LUX_B2C
                m=self.TSL2561_LUX_M2C
            elif (ratio <= self.TSL2561_LUX_K3C):
                b=self.TSL2561_LUX_B3C
                m=self.TSL2561_LUX_M3C
            elif (ratio <= self.TSL2561_LUX_K4C):
                b=self.TSL2561_LUX_B4C
                m=self.TSL2561_LUX_M4C
            elif (ratio <= self.TSL2561_LUX_K5C):
                b=self.TSL2561_LUX_B5C
                m=self.TSL2561_LUX_M5C
            elif (ratio <= self.TSL2561_LUX_K6C):
                b=self.TSL2561_LUX_B6C
                m=self.TSL2561_LUX_M6C
            elif (ratio <= self.TSL2561_LUX_K7C):
                b=self.TSL2561_LUX_B7C
                m=self.TSL2561_LUX_M7C
            elif (ratio > self.TSL2561_LUX_K8C):
                b=self.TSL2561_LUX_B8C
                m=self.TSL2561_LUX_M8C
        elif (self.TSL2561_PACKAGE_T_FN_CL == 1):
            if ((ratio >= 0) and (ratio <= self.TSL2561_LUX_K1T)):
                b=self.TSL2561_LUX_B1T
                m=self.TSL2561_LUX_M1T
            elif (ratio <= self.TSL2561_LUX_K2T):
                b=self.TSL2561_LUX_B2T
                m=self.TSL2561_LUX_M2T
            elif (ratio <= self.TSL2561_LUX_K3T):
                b=self.TSL2561_LUX_B3T
                m=self.TSL2561_LUX_M3T
            elif (ratio <= self.TSL2561_LUX_K4T):
                b=self.TSL2561_LUX_B4T
                m=self.TSL2561_LUX_M4T
            elif (ratio <= self.TSL2561_LUX_K5T):
                b=self.TSL2561_LUX_B5T
                m=self.TSL2561_LUX_M5T
            elif (ratio <= self.TSL2561_LUX_K6T):
                b=self.TSL2561_LUX_B6T
                m=self.TSL2561_LUX_M6T
            elif (ratio <= self.TSL2561_LUX_K7T):
                b=self.TSL2561_LUX_B7T
                m=self.TSL2561_LUX_M7T
            elif (ratio > self.TSL2561_LUX_K8T):
                b=self.TSL2561_LUX_B8T
                m=self.TSL2561_LUX_M8T
        #endif

        temp = ((channel0 * b) - (channel1 * m))

        # Do not allow negative lux value */
        if (temp < 0):
            temp = 0

        # Round lsb (2^(LUX_SCALE-1)) */
        temp += (1 << (self.TSL2561_LUX_LUXSCALE-1))

        # Strip off fractional portion */
        lux = temp >> self.TSL2561_LUX_LUXSCALE;

        # Signal I2C had no errors */
        if (self._debug == True): print "calculateLux_end"
        return lux

'''
#**************************************************************************/
#   Gets the most recent sensor event
#**************************************************************************/
void Adafruit_TSL2561::getEvent(sensors_event_t *event)
{
  uint16_t broadband, ir;
 
  # Clear the event */
  memset(event, 0, sizeof(sensors_event_t));
 
  event->version   = sizeof(sensors_event_t);
  event->sensor_id = _tsl2561SensorID;
  event->type      = SENSOR_TYPE_LIGHT;
  event->timestamp = 0;

  # Calculate the actual lux value */
  getLuminosity(&broadband, &ir);
  event->light = calculateLux(broadband, ir);
}

#**************************************************************************/
#   Gets the sensor_t data
#**************************************************************************/
void Adafruit_TSL2561::getSensor(sensor_t *sensor)
{
  # Clear the sensor_t object */
  memset(sensor, 0, sizeof(sensor_t));

  # Insert the sensor name in the fixed length char array */
  strncpy (sensor->name, "TSL2561", sizeof(sensor->name) - 1);
  sensor->name[sizeof(sensor->name)- 1] = 0;
  sensor->version     = 1;
  sensor->sensor_id   = _tsl2561SensorID;
  sensor->type        = SENSOR_TYPE_LIGHT;
  sensor->min_delay   = 0;
  sensor->max_value   = 17000.0;  /* Based on trial and error ... confirm! */
  sensor->min_value   = 0.0;
  sensor->resolution  = 1.0;
}
'''

LightSensor = Adafruit_TSL2651()
LightSensor.enableAutoGain(True)
while True:
    print LightSensor.calculateLux(), " Lux"


For completeness: I found other solutions for the TSL2561 and RPi:
https://github.com/janheise/TSL2561
http://www.raspberrypi.org/phpBB3/viewt ... 8&p=401408
But those didn't work for me.

I hope you can use the code. I hope further that I'll find some time to clean up the script.

-Mario

schwabbbel
 
Posts: 10
Joined: Wed Oct 09, 2013 8:34 am

Re: TSL2561 and Raspberry Pi

by schwabbbel on Fri Oct 11, 2013 2:19 pm

What I forgot to mention:
The output of the script is pretty similar to the output of the arduino running the original script. And those values are both pretty close the reading of my Luxmeter. I also tried an Luxmeter app for my smartphone but that didn't work quite well.
That's all.

- Mario

schwabbbel
 
Posts: 10
Joined: Wed Oct 09, 2013 8:34 am

Re: TSL2561 and Raspberry Pi

by dalgibbard on Fri Nov 01, 2013 7:53 am

Hi all,
@schwabbbel - I gave your code a go last night, seems to work a treat!
One thing i have noticed when testing it with a PWM controlled LED is that the sample rate is so quick that it can (and will) report varying values in those scenarios; As i only ever really want a single value returned per-call (just a number too; i already know it's giving me Lux :) ), I worked around this by simply taking 100 pieces of sample data, and averaging them- my code is a bit shonky, but it comes close enough for my purposes (Add this towards the bottom... replaces the standard "while True:" loop):

Code: Select all | TOGGLE FULL SIZE
LightSensor = Adafruit_TSL2651()
LightSensor.enableAutoGain(True)
# See if "loop" has been passed as an arg.
try:
    arg = sys.argv[1]
    if ( arg == "loop" ):
        while True:
            try:
                print (LightSensor.calculateLux())
            except KeyboardInterrupt:
                quit()
    else:
        print ("Invalid arg(s):", sys.argv)
except IndexError:
    # Set initial vars
    count = 0
    luxavgtotal = 0
    # Number of tests to average over
    testavg = int(100)
    # Create a cumulative total of values for 'testavg' tests
    while True:
        capture = LightSensor.calculateLux()
        luxavgtotal = capture + luxavgtotal
        count += 1
        # Once we reach the number of required tests, work out the average
        if ( count >= testavg ):
            luxavg = round(luxavgtotal / testavg)
            # Must set the print value as int(), else Python2.X prints <value>.0 instead of just <value>
            print (int(luxavg))
            break


Aside from this, i've also been going through the code to make it python3 compatible...
I've compiled py-smbus using the details posted by catmaker at http://www.raspberrypi.org/phpBB3/viewt ... 32&t=22348
Swapped out all print statements in both "lux.py" and the Adafruit Library code to be wrapped with brackets (ie. 'print "Hello"' became 'print("Hello")') and swapping all of the Error statements in the Adafruit library which referenced "err"; for example:
Code: Select all | TOGGLE FULL SIZE
except IOError, err:

becomes
Code: Select all | TOGGLE FULL SIZE
except IOError as err:


So far so good. Then i got the following from lux.py:
Code: Select all | TOGGLE FULL SIZE
Traceback (most recent call last):
  File "./lux.py", line 531, in <module>
    capture = LightSensor.calculateLux()
  File "./lux.py", line 399, in calculateLux
    ratio = (ratio1 + 1) >> 1
TypeError: unsupported operand type(s) for >>: 'float' and 'int'


I had a quick go at wrapping "ratio1" in the last command with int() to convert it for the calculation:
Code: Select all | TOGGLE FULL SIZE
ratio = (int(ratio1) + 1) >> 1


Problem solved.
dalgibbard
 
Posts: 1
Joined: Fri Nov 01, 2013 7:16 am

Re: TSL2561 and Raspberry Pi C Code WiringPi

by Pasquale88 on Mon Jan 13, 2014 12:13 pm

do you have a C code written with wiringpi?
Pasquale88
 
Posts: 3
Joined: Mon Dec 02, 2013 12:38 pm

Re: TSL2561 and Raspberry Pi

by albert789 on Thu Feb 06, 2014 6:37 pm

Hi. Thanks for your code schwabbbel. I tested it and everything works great but when I run it after Dallas 18B20 1-wire thermometer I always get zero form calculateLux().

TSL2561 code:
Code: Select all | TOGGLE FULL SIZE
LightSensor = Adafruit_TSL2651()
LightSensor.enableAutoGain(True)
while True:
    lux = LightSensor.calculateLux()
    print lux
    time.sleep(5)


Normal readings:
402
407
418
433
444


Readings after Dallas 18B20:
0
0
0
0
0


Dallas 18B20 code:
Code: Select all | TOGGLE FULL SIZE
import os
import glob
import time

os.system('modprobe w1-gpio')
os.system('modprobe w1-therm')

base_dir = '/sys/bus/w1/devices/'
device_folder = glob.glob(base_dir + '28*')[0]
device_file = device_folder + '/w1_slave'

def read_temp_raw():
    f = open(device_file, 'r')
    lines = f.readlines()
    f.close()
    return lines

def read_temp():
    lines = read_temp_raw()
    while lines[0].strip()[-3:] != 'YES':
        time.sleep(0.2)
        lines = read_temp_raw()
    equals_pos = lines[1].find('t=')
    if equals_pos != -1:
        temp_string = lines[1][equals_pos+2:]
        temp_c = float(temp_string) / 1000.0
        #temp_f = temp_c * 9.0 / 5.0 + 32.0
        return temp_c

while True:
    tmp = read_temp()
    print tmp
    time.sleep(5)


Any ideas?

PS. Sorry for my English :wink:

albert789
 
Posts: 2
Joined: Thu Feb 06, 2014 6:13 pm

Re: TSL2561 and Raspberry Pi

by schwabbbel on Thu Feb 06, 2014 7:12 pm

Hi Albert,

I am glad you could make some use uf the code. As I understand you are using the code from "Adafruit's Raspberry Pi Lesson 11" to read the temperature sensor. What do you mean by "running after"?
Do you run the script for the Dallas 18B20 sensor, let it end and start the script for the TSL2561 or did you merge it in one file? If it is all in one file could you post that? Is there any other output from the console beside the sensor values?

Best, schwabbbel

P.S. If it would help, we could discuss in German.

schwabbbel
 
Posts: 10
Joined: Wed Oct 09, 2013 8:34 am

Re: TSL2561 and Raspberry Pi

by albert789 on Thu Feb 06, 2014 7:55 pm

Thank you for your answer.

schwabbbel wrote:Do you run the script for the Dallas 18B20 sensor, let it end and start the script for the TSL2561

That is exactly what I am doing.

schwabbbel wrote:Is there any other output from the console beside the sensor values?

When I am running TSL2561 script first with the debug flag set to True:
Code: Select all | TOGGLE FULL SIZE
__init__
__init___end
enableAutoGain
enableAutoGain_end
calculateLux
getLuminosity
begin
read8
setIntegrationTime
enable
enable_end
write8
write8_end
disable
disable_end
setIntegrationTime_end
setGain
enable
enable_end
write8
write8_end
disable
disable_end
setGain_end
disable
disable_end
begin_end
getData
enable
enable_end
read16
read16
disable
disable_end
getData_end
setGain
enable
enable_end
write8
write8_end
disable
disable_end
setGain_end
getData
enable
enable_end
read16
read16
disable
disable_end
getData_end
getData
enable
enable_end
read16
read16
disable
disable_end
getData_end
getLuminosity_end
calculateLux_end
112

and after running temperature sensor script:
Code: Select all | TOGGLE FULL SIZE
__init__
__init___end
enableAutoGain
enableAutoGain_end
calculateLux
getLuminosity
begin
read8
getData
enable
enable_end
read16
read16
disable
disable_end
getData_end
setGain
begin
read8
enable
enable_end
write8
write8_end
disable
disable_end
setGain_end
getData
enable
enable_end
read16
read16
disable
disable_end
getData_end
getData
enable
enable_end
read16
read16
disable
disable_end
getData_end
getLuminosity_end
calculateLux_end
0


After that I need to reboot Raspberry Pi and if I don't run 18B20 script or type in console "sudo modprobe w1-gpio" everything is working fine.

schwabbbel wrote:P.S. If it would help, we could discuss in German.

Pretty close - I am from Poland but unfortunately I don't speak German :wink:

albert789
 
Posts: 2
Joined: Thu Feb 06, 2014 6:13 pm

Please be positive and constructive with your questions and comments.