Re: TSL2561 and Raspberry Pi

by scortier on Thu Feb 28, 2013 9:40 am

Agreed. This is definitely a team effort, as I only started learning Python a couple months ago! Static, if you mean integration time, then I have it set for 402ms. In the initialization, register 0x81 is set to 0x11. Instead, I have changed 0x11 to 0x1A so the gain is still 16, but now the integration time is changed. See if you get different values. I fiddled around with the register with different integration times, which changes the output values considerably.
scortier
 
Posts: 13
Joined: Mon Jan 14, 2013 1:47 pm

Re: TSL2561 and Raspberry Pi

by Static on Thu Feb 28, 2013 9:42 am

How often are you polling the sensors? I'm only polling mine once a minute, right now.
Static
 
Posts: 178
Joined: Thu Dec 23, 2010 5:21 pm

Re: TSL2561 and Raspberry Pi

by scortier on Thu Feb 28, 2013 9:45 am

Pretty much every time it does a conversion, I'm polling. I just have a while (True) function that keeps grabbing the value and printing.
scortier
 
Posts: 13
Joined: Mon Jan 14, 2013 1:47 pm

Re: TSL2561 and Raspberry Pi

by Static on Thu Feb 28, 2013 12:20 pm

OK. I'm working on this, but I hit a snag, again.
I can't seem to clone my drive. I'm just trying to duplicate my SD card so I'm working with the same system on both Pi's.
Static
 
Posts: 178
Joined: Thu Dec 23, 2010 5:21 pm

Re: TSL2561 and Raspberry Pi

by Static on Fri Mar 01, 2013 12:33 pm

I set up the second bus on my first Pi today. I'm getting really bizarre results, and now that I'm looking at my logs, I've been getting weird results for a week.

Specifically, since I updated the code with the ratio correction, it looks like the sensor will read really high, and then drop to 51.1173(lots of zeros)3. Then, at the end of the day, the system will spike again, and then drop very low.

Initially I set up the new sensor inside of a box, with a single white LED poking through.
I let the sensor run for an hour and fifteen minutes (75 minutes) and then turned on the LED. The whole time I got the strange 51.1173(lots of zeros)3 value. Then I turned the LED on. I was pretty surprised when I saw the exact same result when I graphed it out an hour later.

I'm going to go run errands real quick, when I get back I'll tear through the code and see if I did something strange.
Static
 
Posts: 178
Joined: Thu Dec 23, 2010 5:21 pm

Re: TSL2561 and Raspberry Pi

by Static on Fri Mar 01, 2013 2:15 pm

Attached are the two different light sensor files. Each record is a minute log. Each record is an elapsed time. Left side of the graph is oldest data, newest data is on the right (I haven't fixed the format for that, yet).

On the outside light sensor graph (LightChart.png) you can see that at around record 5000 the character of the recorded light changes. That's where I changed the code with the correct ratio. I'm going to look now to see if I "bumped" something else.
Attachments
LightChart.png
Outside Light Sensor
LightChart.png (29.48 KiB) Viewed 1393 times
LightChart2.png
Inside Light Sensor
LightChart2.png (12.4 KiB) Viewed 1393 times
Static
 
Posts: 178
Joined: Thu Dec 23, 2010 5:21 pm

Re: TSL2561 and Raspberry Pi

by csalty on Fri Mar 01, 2013 4:49 pm

I was going to upload my code after I did some proofing. I'm really new at Python and my additions are very minor. Most start below "def luxread...." I already had data streaming to Google doc working on my RasPi (thanks to the Adafruit tutorial). So far my script and sensor is working pretty well with at https://cosm.com/feeds/105257

Here is the code:
Code: Select all
#!/usr/bin/python

import sys
import smbus
import time
import eeml
from Adafruit_I2C import Adafruit_I2C
from array import *

# info about the cosm.com feed
API_KEY = '  '  # replace with your PRIVATE cosm KEY
FEED=105257  # replace with your feednumber, this isn't private
API_URL = '/v2/feeds/{feednum}.xml' .format(feednum = FEED)


### 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(type, 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()

    if ambient == 0:  # in the dark you cant divide by 0 in the next calculation
        ambient = 0.0001  # so I set it to a small number
       
    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

    #  I want to know the values for IR, ambient, and lux
       
    if (type==1):
       return IR
    elif (type==2):
       return ambient
    elif (type==3):
       return lux
   

def getdata():
    """read sensor and print the data"""
    irbuffer=[]      # place to store multiple IR readings type==1
    ambientbuffer=[] # place to store multiple Ambient readings type==2
    luxbuffer=[]     # place to store multiple Lux readings type==3

    # number of reads to save in the bufer
    for x in range(1,20):
        irbuffer.append(luxread(1))
        luxbuffer.append(luxread(3))
        ambientbuffer.append(luxread(2))
   
    # calculate the average value within the buffer
    a = (sum(ambientbuffer) / len(ambientbuffer))
    b = int((sum(luxbuffer) / len(luxbuffer))) # convert to integer
    c = (sum(irbuffer) / len(irbuffer))
   
    # Open up data stream to cosm with your URL and key
    pac = eeml.Pachube(API_URL, API_KEY)

    # data to send to cosm.  Stuff in the '' is important because these are the titles
    # used in the COSM data stream.  Change to whatever you like.
    pac.update(eeml.Data('Lux', b))
    pac.update(eeml.Data('IR', c))
    pac.update(eeml.Data('Ambient', a))

    # send data to cosm
    pac.put()

    # in case you want to get the data to the terminal
    return("Lux: %.2f Ambient: %.2f IR: %.2f" % (b, a, c)) 

print("Program started, logging to COSM, ctrl-C to end")  # a startup service message

try:
    while 1:
        getdata()
        # print(getdata())  uncomment if you want to see the readings in the terminal
        time.sleep(60)   # change the timing of the feed
except KeyboardInterrupt:
        print "program has ended"


I'm experimenting with the length of data within the buffer and if it smooths out any data spikes. Maybe there is a better statistic that can be used like median value. My next project is to add button control that allows one to override and change the integration and gain. I have an Arduino code that does this but not for Python. Cheers!
csalty
 
Posts: 27
Joined: Sat Feb 23, 2013 7:32 pm

Re: TSL2561 and Raspberry Pi

by csalty on Fri Mar 01, 2013 6:37 pm

Static: is your data captured during the brightest times of the day? Maybe the sensor is not able to handle the sun's full intensity. BTW, Adafruit just updated their Arduino library for the TSL2561. It's definitely changed from the last library. They incorporated your idea of an autogain feature.
csalty
 
Posts: 27
Joined: Sat Feb 23, 2013 7:32 pm

Re: TSL2561 and Raspberry Pi

by Static on Fri Mar 01, 2013 8:07 pm

We're looking at two locations for my data. My office, which is going to be consistent between two levels: light on and light off.
The other sensor is just outside my office window. It should be getting normal daylight cycles with occasional variances for trees and clouds.

I'm just not seeing either of them.

At 1607, local, the readings went from 51.1163(lots of zeros)3 to 2064.3525, on both sensors, and stayed steady.
At 1703, local, the readings started to trend down, but at differing rates. They continue to trend down until:
At 1841, local, the readings stop at 0.315, and remain constant. I just lit the indoor sensor up with a 100 lumen tactical light. It didn't budge.

I wish I'd come back to check on things sooner. I've come back from a bit of a celebration and half a bottle of wine. I'm not sure I'm up to tackling coding issues right now...

Still, like an idiot, I'm going to try.
Static
 
Posts: 178
Joined: Thu Dec 23, 2010 5:21 pm

Re: TSL2561 and Raspberry Pi

by Static on Fri Mar 01, 2013 8:30 pm

....and I should drink more often.

I didn't put "self" in my "readLux" function. It was passing the bus number 1 to the function by default.
I had also crippled the auto-ranging feature in my last code by doing a sloppy "Find/Replace" edit.

I just fixed those two things. The interior sensor appears to be reading correctly (the external sensor is not easy to get to. I'm not leaning out the window in my current state).
I'm going to let the data run for 24 hours, and I'll post up tomorrow.

I think there is some humble pie in the house, somewhere...
Static
 
Posts: 178
Joined: Thu Dec 23, 2010 5:21 pm

Re: TSL2561 and Raspberry Pi

by adafruit_support_mike on Sat Mar 02, 2013 12:02 am

Static wrote:I think there is some humble pie in the house, somewhere...

Preachin' to the choir, brother. ;-)

In all honesty, while I feel your pain, threads like this are a HUGE benefit for people new to the game. Makers with the courage to stand up and admit being human help lift our younger cousins over the barrier labeled "I make mistakes, so I must not be any good."

I spent a couple of hours last week building a 'dead bug' circuit to test a design. Got it all soldered together, plugged it in, and gradually noticed the smell of a chip burning. I'd forgotten that when you flip an IC onto its back, the VCC and GND pins move to different positions.
When you void a product warranty, you give up your right to sue the manufacturer if something goes wrong and accept full responsibility for whatever happens next. And then you truly own the product.
User avatar
adafruit_support_mike
 
Posts: 7068
Joined: Thu Feb 11, 2010 1:51 pm

Re: TSL2561 and Raspberry Pi

by Static on Sat Mar 02, 2013 5:45 am

mstone@yawp.com wrote:I spent a couple of hours last week building a 'dead bug' circuit to test a design. Got it all soldered together, plugged it in, and gradually noticed the smell of a chip burning. I'd forgotten that when you flip an IC onto its back, the VCC and GND pins move to different positions.


LOL!
It was very useful to wake up and read this, as I've done the same thing twice now.
I'm still pretty new to the entire electronics and computer programming realm. Hell, I've been in medicine for over 20 years, I still learn something new every day (typically I announce it and point it out to my students).

Back to business (but by all means, share your anecdotes that underline that you're only human. We all need them from time to time).

The logger worked all night (stability hasn't been an issue). It's been reading a fraction of a lux when the lights are out. It's at 16 lux when my computer screens are on. It's around 115 when everything in the office is on. That signal is pretty noisy because it's a noisy environment. Computers are playing, people are moving, etc.

So a couple of things contributed to the bizarre data, both were sloppiness.

The first was that I was sloppy with my "Find/Replace" command. This caused the sensors to work fine in the low to high range, but drop their sensitivity to nothing at a certain "saturation". At this point the transduced signal dropped to zero (almost zero in relation to the thousands it should have been), and stayed there while the light falling on the sensor was high. Then, the sensor's saturation dropped past a certain threshold and the transduced signal went through the roof again (the graph looks like vampire fangs from the lower jaw), but steadily decreased. The minimum transduced signal levels were identical because they were driven to that level by the incorrectly applied formula (my error).

The interesting thing is, I got slightly different data from each sensor. It looked like I was polling two different sensors from the same light condition.
I had incorrectly created the "readLux" part of the library. I hadn't put "self" in the passed variables to the function. The function was calling a default condition I had written into the code, and was only looking to Bus1 for data.
The numbers I was getting out were due to the fact that we're taking samples about half a second apart. We're measuring a changing system. That half a second is enough to get a slightly different reading, which is exactly what was observed.

I've got to bounce in to town and see the family. I'll be back on (late) tonight. I'll post some more details.
I didn't know anyone was reading this, except csalty and scortier.
Static
 
Posts: 178
Joined: Thu Dec 23, 2010 5:21 pm

Re: TSL2561 and Raspberry Pi

by Static on Sat Mar 02, 2013 11:19 pm

That did it!
The graphs that I'm getting look much closer to reality than what was happening before.

Lightchart.png shows the light conditions outside my window (most recent time on the left of the graph). While there seem to be some spurious dropouts to zero or near zero, it looks much better than before.
You can see the same thing in Lightchart2.png. There are some dropouts, but it is much better than it was before. Sensor averaging or "drop discrimination" might be able to help with that.

The interior sensor (lightchart2) should be pretty stable throughout the day. I was in the office briefly this morning, and the big spike is my girlfriend coming home from work before going out to grab me.

Here's the TSL2561 code that I'm using, with Python3.x:
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 TSL2561:
    i2c = None

    def __init__(self, address=0x39, bus =smbus.SMBus(1), debug=False, pause=0.5):
        self.i2c = Adafruit_I2C(address, bus, debug)
        self.address = address
        self.pause = pause
        self.debug = debug
        self.bus = bus

        self.i2c.write8(0x80, 0x03)     # enable the device
        self.i2c.write8(0x81, 0x16)     # 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 readLux(self, address = 0x39, debug = False, autorange = True):
        """Grabs a lux reading either with autoranging or without"""
        LuxSensor = TSL2561(0x39, self.bus, 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(readLux())
       

Attachments
LightChart2.png
Inside Sensor
LightChart2.png (24.41 KiB) Viewed 1300 times
LightChart.png
Outside Sensor
LightChart.png (34.17 KiB) Viewed 1300 times
Static
 
Posts: 178
Joined: Thu Dec 23, 2010 5:21 pm

Re: TSL2561 and Raspberry Pi

by csalty on Sun Mar 03, 2013 3:02 am

I didn't know anyone was reading this, except csalty and scortier.
:D
Too funny!

This is good stuff I just wished I knew what the heck it all means. I can cobbler code together if someone has already done the dirty work. Other than that I'm pretty clueless.

So, I don't understand why you need "self". I put the def readLux outside of the class. There is no "self" in the code I'm using for def readLux. My script seems to run pretty well. My readings change a bit because of the sun shinning through the trees outside my house. Pretty normal. Maybe for your application you could add some type of data smoothing function. Maybe that will mellow out your spikes. Or you have a bad connection to your sensors.

I'm particularly interested in the IR readings from this sensor because we are doing some scientific studies on photosynthetic bacteria that grow on infrared light, 850 nm to be exact. I built a hand held meter with an Arduino. The TSL2561 works great for standardizing the intensities of our LED arrays and also for filament lights. Now I'm hooked on the Raspberry Pi and all the I2C sensors! Each week I keep buying more stuff from Adafruit.com. The "out of stock notice" on their products reminds me of food rationing.

Cheers!
csalty
 
Posts: 27
Joined: Sat Feb 23, 2013 7:32 pm

Re: TSL2561 and Raspberry Pi

by Static on Sun Mar 03, 2013 4:09 am

The "readLux" function of the code is supposed to execute the function as part of the TSL2561 class. (Be real careful reading too much in to what I'm saying here, I'm just barely putting this stuff in to practice. You can already see the screw-up and learning experience I've already provided).
Inside of a Class, the first variable passed to a function (the first item inside the parentheses) is traditionally (Pythonically) "self". In reality it doesn't matter what the first thing is actually called (I think it needs to be consistent throughout the class). By Python convention it is "self".
If I understand it correctly, "self" allows the function to refer to the class variables that house the class functions.
There are some pretty good books out there (I know, I know, more reading). I pulled Mark Pilgrim's book down "Dive into Python 3", and it looks pretty good. The only issue is that Classes are chapter 7. He does a great job of building on his previous examples. The book is good, but you kinda have to start from chapter 1.
http://getpython3.com/diveintopython3/

However, it appears to be free, online (Mr. Pilgrim, I'll buy you a beer if I ever meet you. At least one).

There are other books that will let you just jump into Classes and Objects, but I haven't seen one that just "conveys it". This is one of the areas where Python is incredibly powerful. You need to work at it, but once you get it, it mostly clicks (says the pure amateur, me).

I've run in to the "out of stock notice" from Adafruit. I also got my shipment on time, somehow, in the aftermath of Sandy. That impressed me (I'm an emergency response guy, by trade).
The big thing that does it for me is this board and the technical support. Yeah, I pay a little bit more for things from here. I'll be the first to admit that sometimes I have to buy elsewhere, or find alternate means of procurement (I kit bash and hack things together, that's part of what we do). But these folks do an incredible job. I tried playing with this stuff for decades, and just wasn't getting it. A little willingness to fail (Blowing $30 worth of parts hurts, but isn't damaging), a lot of willingness to read and learn, and some quiet encouragement from the staff here has worked miracles.
Then coming on to the board here and writing things out has really impressed me. You folks are building really cool....stuff (ok, I'm not allowed to use certain words? Words I say to my students or in front of someone else's mother?). Some of those things are utterly inane (why do you do it? Because it's there). Others are building gadgets that would cost hundreds to thousands of dollars (I'm looked for exotic monitoring equipment when I was writing my undergraduate thesis). You're critically thinking, getting a little uncomfortable, and doing really cool things. Some of these things are probably out of your realm, and that's just so freakin' awesome.

I'm kinda jealous of these projects, but really glad you're writing about them.
Static
 
Posts: 178
Joined: Thu Dec 23, 2010 5:21 pm