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.
Locked
lenos
 
Posts: 2
Joined: Mon Mar 04, 2013 3:31 am

Re: TSL2561 and Raspberry Pi

Post by lenos »

Static wrote:
[email protected] wrote: I didn't know anyone was reading this, except csalty and scortier.
i just wanted you guys to know that i follow the thread since it started. my english and python coding skills arent that good as yours, but hey - im still learning.

atm, we use the python code provided here to monitor various fluorescent lamps (which we use to grow algae). so our idea was:
fluorescent tubes lose light intensity with time to time. the monitored output over a couple of month shown on a nice graph could eventually say us the best moment to replace the lamps.

thank you all for sharing your work here, without you guys our project would still be in our minds. now it does an actual job/task - i hope so. :)

[EDIT]
ive run into problems with smbus using python 3.2 (installed from = wheezy main contrib non-free rpi). under 2.7 smbus works like a charm.
all i know is that the package 'python-smbus' is only usable with python 2.x, after googleing around i found this thread here, im still compiling atm, so i cant say if this will work then.
did i miss something? how did you get smbus work with python 3.2?

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

Re: TSL2561 and Raspberry Pi

Post by static »

Your english is just fine, and I'm going to bet your Python skills are on par with mine.

I think smbus just worked, but I'm using the Adafruit Occidentalis build. It's built off of Rasberry Wheezy, but it's not the same. I seem to remember that I had issues getting smbus to work on Wheezy, but not Occidentalis.
I switched to Occidentalis almost immediately after playing with the Raspberry Pi. I've never had an issue with it, and it let me play with the GPIO pins without much effort. I know Adafruit is calling it "Beta", or a "work in progress", but it really seems to flat-out work.
The link for Occidentalis is on this page. If you have an extra SD card, it's worth throwing on there and checking it out:

http://learn.adafruit.com/adafruit-rasp ... g-an-image

The sun's coming up over here, and as I'm writing this, I'm looking at my terminal window. I'm getting weird vacillations from 1171.0755 to 28.9980600000000024 on my external sensor. The interior sensor seems to be reading consistently (1 erroneous value out of 100, where it drops to an integer 0). The exterior sensor is using the same code as the interior one. I can't figure out why it's performing like that, unless it has to do with the cold or the increase in sunlight. I'm getting several readings at one level, then it will switch to the other for several readings, and then back. It looks like it started when the light level approached 1171 (the levels look like they raised normally, without issue).

This is an issue that wouldn't be fixed by averaging. I can't get the inside sensor up to that high a light level. I'm not sure I have an intense enough portable light.

lenos
 
Posts: 2
Joined: Mon Mar 04, 2013 3:31 am

Re: TSL2561 and Raspberry Pi

Post by lenos »

thank you static!

[EDIT]
I seem to remember that I had issues getting smbus to work on Wheezy, but not Occidentalis.
i got the same errors with smbus and python3 on Occidentalis v0.2 - are you using v0.1?

btw. learnpythonthehardway.org is also i nice tutorial for starters, although the title dosen't suggest that. :)

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

Re: TSL2561 and Raspberry Pi

Post by static »

I'm pretty sure that I'm using 0.2. I reinstalled everything within the last two months.

I'm not competent to diagnose the smbus issue (yet). I'm hoping someone else will be able to offer some help. I had really good feedback from the folks over at RaspberryPi.org, if no one here has ideas.

Can someone check the code regarding the auto-ranging (where the gain is automatically adjusted for high light conditions)? I don't have any notes on how that code was arrived at, and looking at it and the spec sheet for the TSL2561, I'm not sure the values and orders of operation are correct.

csalty
 
Posts: 27
Joined: Sat Feb 23, 2013 8:32 pm

Re: TSL2561 and Raspberry Pi

Post by csalty »

Hello Static. Thanks for the explanation regarding the use of "self". I totally understand now. After doing some additional reading on the subject of classes, it is all starting to make much more sense. I should really get a python book.
The sun's coming up over here, and as I'm writing this, I'm looking at my terminal window. I'm getting weird vacillations from 1171.0755 to 28.9980600000000024 on my external sensor.
Interesting you are getting spikes from outside but not from your inside sensor. Maybe you are detecting solar flare activity :D I was getting some strange results with my Arduino version of the sensor, directly pointing at the sun. With inside use of the Pi and Arduino sensors, I get pretty consistent readings. Not sure what's going on with full sun light. After reading the datasheet in more detail, I think that the sensors are really meant to detect ambient light conditions and control the dimming of LCDs and displays in response to changing light conditions
The TSL256x is intended for use in ambient light detection applications such as display backlight control, where adjustments are made to display brightness or contrast based on the brightness of the ambient light, as perceived by the human eye. pg 22 of the datasheet
Check this out too regarding how the lux calculation was calibrated:
The formulas shown above were obtained by optical testing with fluorescent and incandescent light sources, and apply only to open-air applications. Optical apertures (e.g. light pipes) will affect the incident light on the device. pg 22
Looks calculations are not going to be that accurate using sunlight. I bet one could recalibrate the sensor using measurements from a TSL and a good lux meter done over different times of the day in full sunlight. Maybe I'll give that a try.

-csalty

csalty
 
Posts: 27
Joined: Sat Feb 23, 2013 8:32 pm

Re: TSL2561 and Raspberry Pi

Post by csalty »

Lenos: I'm using the TSL2561 for similar purposes, growing purple sulfur bacteria on 850 nm IR LEDs, in the dark. The TSL2561 sensor works perfectly for our application. We can adjust the intensities of our LED arrays to match the IR given off by incandescent light sources. The growth of the microbes are amazingly similar with infrared compared to standard light bulbs.

Good luck with your project and if you have any code modifications please post them.

-csalty

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

Re: TSL2561 and Raspberry Pi

Post by static »

I've got to finish a writing contract today, then I'm going to take a look at the code again. I feel like this is a ratio issue, and I'm implementing the auto-ranging incorrectly. Whenever a ton of light falls on the sensor, it bounces between those two, very specific, values.

csalty
 
Posts: 27
Joined: Sat Feb 23, 2013 8:32 pm

Re: TSL2561 and Raspberry Pi

Post by csalty »

Waiting to see what you come up with.

I had to modify my code because COSM changed how they acuqire data from the eeml module. So I switched to petervizi's eeml python library but there is an issue with it and COSM: you have to use an integer as your ID's for the datafeed. Bummer. While messing around with the new eeml update, I came up with a newer version of my code. I think it is a bit faster than the one I posted earlier.

Code: Select all

#!/usr/bin/python

import sys
import smbus
import time
import eeml   # uses pertervizi's python-eeml
import eeml.datastream
import eeml.unit
from Adafruit_I2C import Adafruit_I2C
from array import *
from eeml.datastream import CosmError

# info about the cosm.com feed
API_KEY = 'key goes here'  # replace with your private KEY 
FEED=105257  # replace with your feednumber
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 ambient
    elif (type==2):
        return IR
    elif (type==3):
        return lux

def getIRbuffer(size):
    """read IR sensor and print the data"""
    buffer=[]      # place to store multiple IR readings type==1

    # number of reads to save in the bufer
    for x in range(0,size):
        buffer.append(luxread(1))

    # calculate the average value within the buffer
    a = (sum(buffer) / len(buffer))

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

def getAmbientbuffer(size):
    """read ambient sensor value and print the data"""
    buffer=[] # place to store multiple Ambient readings type==2
    for x in range(0,size):
        buffer.append(luxread(2))

    a = (sum(buffer) / len(buffer))

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

def getLuxbuffer(size):
    """read Lux sensor make a buffer and print the data"""
    buffer=[]     # place to store multiple Lux readings type==3
    for x in range(0,size):
        buffer.append(luxread(3))

    a = int((sum(buffer) / len(buffer))) # convert to integer

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

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


while(True):  # specify the number of readings in the buffer
    amval= getAmbientbuffer(10)
    irval= getIRbuffer(10)
    luxval= getLuxbuffer(10)
        
#    print (amval, irval, luxval)  # uncomment if you want to see the data in the terminal

    # Open up data stream to cosm with your URL and key
    pac = eeml.datastream.Cosm(API_URL, API_KEY)
    pac.update(eeml.Data(0, amval, tags=('Ambient',)))  # ID's has to be an integer
    pac.update(eeml.Data(1, irval, tags=('Infrared',))) # waiting for update
    pac.update(eeml.Data(2, luxval, tags=('Lux',)))     # to fix this

    # send data to cosm
    pac.put()
I also made a version for using gspread and google docs. I learned about the Google Widget and how you can make a widget like one of those sliding stock windows/quotes. It's much easier to implement relative to COSM. Here's my code so far for the gspread:

Code: Select all

#!/usr/bin/python

import sys
import smbus
import time
import datetime
import gspread
from Adafruit_I2C import Adafruit_I2C
from array import *


### 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 ambient
    elif (type==2):
        return IR
    elif (type==3):
        return lux

def getIRbuffer(size):
    """read IR sensor and print the data"""
    buffer=[]      # place to store multiple IR readings type==1

    # number of reads to save in the bufer
    for x in range(0,size):
        buffer.append(luxread(1))

    # calculate the average value within the buffer
    a = (sum(buffer) / len(buffer))

    return a

def getAmbientbuffer(size):
    """read ambient sensor value and print the data"""
    buffer=[] # place to store multiple Ambient readings type==2
    for x in range(0,size):
        buffer.append(luxread(2))

    a = (sum(buffer) / len(buffer))

    return a

def getLuxbuffer(size):
    """read Lux sensor make a buffer and print the data"""
    buffer=[]     # place to store multiple Lux readings type==3
    for x in range(0,size):
        buffer.append(luxread(3))

    a = int((sum(buffer) / len(buffer))) # convert to integer

    return a


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

while(True):
    amval= getAmbientbuffer(10)  # number of reads in the buffer
    irval= getIRbuffer(10)
    luxval= getLuxbuffer(10)
        
#    print (amval, irval, luxval)  # uncomment to print to the terminal

    # Login with your Google account
    gc = gspread.login('your google email address', 'your password')  # enter login credentials

    # Open a worksheet from spreadsheet with one shot
    wks = gc.open("spreadsheet name goes here").sheet1  # put your spreadsheet name here

    # print the values to the Google Spreadsheet
    values = [datetime.datetime.now(), amval, irval, luxval]
    wks.append_row(values)

    time.sleep(60) #  reading interval

csalty
 
Posts: 27
Joined: Sat Feb 23, 2013 8:32 pm

Re: TSL2561 and Raspberry Pi

Post by csalty »

For what it's worth, here is a new version of the TSL2561 code I've been working on. I post my data from a Raspberry Pi to http://cosm.com for realtime data monitoring. I switched to a json-based method of formatting because of some problems with the eeml module (xml-based). I found a great simple python approach from a Cosm.com forum member. What a genius! See post: http://community.cosm.com/node/910

Latest script:

Code: Select all

#!/usr/bin/python

from Adafruit_I2C import Adafruit_I2C
from array import *
from datetime import datetime
import json
import mechanize
import smbus
import sys
import time

# info about the cosm.com feed
key = 'put your long cosm.com key here'  # replace with your private KEY 
feed='cosm feed number goes here'  # replace with your feednumber

### Original code by bryand, who wrote the code and static who borrowed heavily from/was 
### inspired by.  Visit static's website: medicforlife.blogspot.com
### Thanks to driverblock and the Adafruit team for answering questions on the forum


# ===========================================================================
# Cosm json CLASS  develop by jrheling http://community.cosm.com/node/910
# Also see http://www.netfluvia.org/layer8/?p=175 for code
# ===========================================================================

class PachubeFeedUpdate:

  _url_base = "http://api.pachube.com/v2/feeds/"
  _feed_id = None
  _version = None
  ## the substance of our update - list of dictionaries with keys 'id' and 'current_value'
  _data = None
  ## the actual object we'll JSONify and send to the API endpoint
  _payload = None
  _opener = None

  def __init__(self, feed_id, apikey):
    self._version = "1.0.0"
    self._feed_id = feed_id
    self._opener = mechanize.build_opener()
    self._opener.addheaders = [('X-PachubeApiKey',apikey)]
    self._data = []
    self._payload = {}

  def addDatapoint(self,dp_id,dp_value):
    self._data.append({'id':dp_id, 'current_value':dp_value})

  def buildUpdate(self):
    self._payload['version'] = self._version
    self._payload['id'] = self._feed_id
    self._payload['datastreams'] = self._data

  def sendUpdate(self):
    url = self._url_base + self._feed_id + "?_method=put"
    try:
      self._opener.open(url,json.dumps(self._payload))
    except mechanize.HTTPError as e:
      print "An HTTP error occurred: %s " % e

# ===========================================================================
# Luxmeter CLASS
# ===========================================================================

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


# ===========================================================================
# READING THE SENSOR
# ===========================================================================

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 ambient
    elif (type==2):
        return IR
    elif (type==3):
        return lux

# ===========================================================================
# MAKE DATA BUFFERS
# ===========================================================================

def getIRbuffer(size):
    """read IR sensor and print the data"""
    buffer=[]      # place to store multiple IR readings type==1

    # number of reads to save in the bufer
    for x in range(0,size):
        buffer.append(luxread(1))

    # calculate the average value within the buffer
    a = (sum(buffer) / len(buffer))

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

def getAmbientbuffer(size):
    """read ambient sensor value and print the data"""
    buffer=[] # place to store multiple Ambient readings type==2
    for x in range(0,size):
        buffer.append(luxread(2))

    a = (sum(buffer) / len(buffer))

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

def getLuxbuffer(size):
    """read Lux sensor make a buffer and print the data"""
    buffer=[]     # place to store multiple Lux readings type==3
    for x in range(0,size):
        buffer.append(luxread(3))

    a = int((sum(buffer) / len(buffer))) # convert to integer

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

# ===========================================================================
# UPLOAD THE DATA TO COSM
# ===========================================================================

# a startup service message
now = datetime.now()
print("Program started %s, logging to COSM, ctrl-C to end" % now)

# In the code below the buffer is the number of 
# reads that will be averaged together

while True:
  try:
    # process the ambient sensor value
    amval= getAmbientbuffer(10)
    pfu = PachubeFeedUpdate(feed,key)
    pfu.addDatapoint('Ambient', amval)
    # finish up and submit the data
    pfu.buildUpdate()
    pfu.sendUpdate()

    # process the infrared sensor value
    irval= getIRbuffer(10)
    pfu = PachubeFeedUpdate(feed,key)
    pfu.addDatapoint('Infrared', irval)
    # finish up and submit the data
    pfu.buildUpdate()
    pfu.sendUpdate()

    # process the the lux sensor value
    luxval= getLuxbuffer(10)
    pfu = PachubeFeedUpdate(feed,key)
    pfu.addDatapoint('Lux', luxval)
    # finish up and submit the data
    pfu.buildUpdate()
    pfu.sendUpdate()

    # pause, remove for full speed!
    time.sleep(10)
  except (KeyboardInterrupt, SystemExit):
    raise
  except StandardError:
    print ('ERROR: StandardError')

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

Re: TSL2561 and Raspberry Pi

Post by static »

Sorry I haven't posted in a bit.

I just updated my Adafruit_I2C to the most recent version.

Now, everything is broken.

I'm tracing things through the code, but it looks like it should be handling everything the same (There were a couple of small changes that I needed to make). The values I'm getting are just wrong, across the board. Temperature (two different sensors), pressure, light, all of 'em.

csalty
 
Posts: 27
Joined: Sat Feb 23, 2013 8:32 pm

Re: TSL2561 and Raspberry Pi

Post by csalty »

That stinks. Hope you can work everything out. Do you know what's different about their newer module?

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

Re: TSL2561 and Raspberry Pi

Post by static »

It doesn't look like there is anything there that would change things.
This morning (after the coffee) i'm going to pull the sensor pod back inside.
I'm in the process (Right now) of pulling my other Raspberry Pi.

I'll try swapping out components, and seeing if I can troubleshoot the issue.

From the coding side of things, it doesn't look like there should be a problem.

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

Re: TSL2561 and Raspberry Pi

Post by static »

Boy, was I wrong.

The new Adafruit_I2C.py seemed to be the culprit. It took me this long to try trading out functions, and getting the functionality back that I wanted. I'm not using the stock Adafruit_I2C.py. If other folks are having problems, I can post it up here.

An interesting side effect: Right now, it looks like the light levels are steady. I didn't pop the sensor outside until after noon, but things looked good this afternoon.

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

Re: TSL2561 and Raspberry Pi

Post by static »

I'm still getting the weird "vampire fang" pattern during peak light cycles. The value pegs, then rapidly (over minutes) drops down. Later in the day, the value rapidly climbs (again, over minutes) and then drops down.

huelke
 
Posts: 2
Joined: Thu May 02, 2013 11:57 am

Re: TSL2561 and Raspberry Pi

Post by huelke »

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.

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

Return to “General Project help”