msgbartop
Blog di Bernardino (Dino) Ciuffetti
msgbarbottom

19 Mar 14 TSL2561 light sensor on Raspberry pi in C

After I bought a new TSL2561 digital light sensor from Adafruit, I found that the very cool and small device cannot be accessed directly from linux (rasbian doesn’t have it’s kernel module compiled). Since I didn’t want to cross recompile my whole raspberry pi kernel just to have the tsl2563.ko driver enabled, and since it seems that raspbian does not relase genuine kernel headers to just compile custom kernel modules, I decided to write a user space simple library driver in C.

I found out that Adafruit relases proof of concept libraries written in C++ and python to access its hardware devices, the problem is that the c++ version is ready for arduino but it was not so directly usable for my raspberry pi. It also makes use of an adafruit unified sensor library and other external stuff. Since I am too lazy I decided yesterday to write a new simple library in plain C without external dependencies, just ready for my raspberry pi.

This is the arduino version that inspired me: https://github.com/adafruit/TSL2561-Arduino-Library
This is another cool blog post that inspired me (it now seems dead!!): http://russelldavis.org/2013/03/23/raspberryhunt-part-2/

This is an example:

/* prepare the sensor
(the first parameter is the raspberry pi i2c master controller attached to the TSL2561, the second is the i2c selection jumper)
The i2c selection address can be one of: TSL2561_ADDR_LOW, TSL2561_ADDR_FLOAT or TSL2561_ADDR_HIGH
*/
TSL2561 light1 = TSL2561_INIT(1, TSL2561_ADDR_FLOAT);

/* initialize the sensor */
rc = TSL2561_OPEN(&light1);

/* sense the luminosity from the sensor (lux is the luminosity taken in "lux" measure units)
the last parameter can be 1 to enable library auto gain, or 0 to disable it */
rc = TSL2561_SENSELIGHT(&light1, &broadband, &ir, &lux, 1);

TSL2561_CLOSE(&light1);

Compile:

gcc -Wall -O2 -o TSL2561.o -c TSL2561.c
gcc -Wall -O2 -o TSL2561_test.o -c TSL2561_test.c
gcc -Wall -O2 -o TSL2561_test TSL2561.o TSL2561_test.o

The output is like this:

root@rasponi:~/test/gpio# ./TSL2561_test
Test. RC: 0(Success), broadband: 141, ir: 34, lux: 12

As you can see it’s very easy at this point to get the light measures in C. Just include TSL2561.c and TSL2561.h inside your project and use the public APIs to setup and sense the IC.

I decided to release the code with the liberal apache v2 license, so feel free to include it into your commercial projects if you like.

It’s useful for me, and I hope that it can be useful to you too. Obviously it comes with absolutely no warranty.

p.s.1: I left the hardware stuff out of this article (just attach +vcc, gnd and i2c bus to the sensor
p.s.2: you have to load two kernel modules to get i2c bus working on you Raspberry pi:

modprobe i2c_bcm2708
modprobe i2c_dev

Ciao, Dino.

TSL2561.c
TSL2561.h
TSL2561_test.c

This is an example on how to use all 3 sensors on the same i2c bus:

#include <stdio.h>
#include <string.h>
#include "TSL2561.h"

int main() {
	int i;
	int rc;
	uint16_t broadband, ir;
	uint32_t lux=0;
	TSL2561 lights[3]; // we can handle 3 sensors
	
	// prepare the sensors
	// (the first parameter is the raspberry pi i2c master controller attached to the TSL2561, the second is the i2c selection jumper)
	// The i2c selection address can be one of: TSL2561_ADDR_LOW, TSL2561_ADDR_FLOAT or TSL2561_ADDR_HIGH
	
	// prepare all sensors
	/* cannot assign that way
	lights[0] = TSL2561_INIT(1, TSL2561_ADDR_LOW);
	lights[1] = TSL2561_INIT(1, TSL2561_ADDR_FLOAT);
	lights[2] = TSL2561_INIT(1, TSL2561_ADDR_HIGH);
	*/
	
	// initialize at runtime instead
	// FIRST SENSOR --> TSL2561_ADDR_LOW
	lights[0].adapter_nr=1;						// change this according to your i2c bus
	lights[0].sensor_addr=TSL2561_ADDR_LOW;				// don't change this
	lights[0].integration_time=TSL2561_INTEGRATIONTIME_402MS;	// don't change this
	lights[0].gain=TSL2561_GAIN_16X;				// don't change this
	lights[0].adapter_fd=-1;					// don't change this
	lights[0].lasterr=0;						// don't change this
	bzero(&lights[0].buf, sizeof(lights[0].buf));			// don't change this
	
	// SECOND SENSOR --> TSL2561_ADDR_FLOAT
	lights[1].adapter_nr=1;						// change this according to your i2c bus
	lights[1].sensor_addr=TSL2561_ADDR_FLOAT;			// don't change this
	lights[1].integration_time=TSL2561_INTEGRATIONTIME_402MS;	// don't change this
	lights[1].gain=TSL2561_GAIN_16X;				// don't change this
	lights[1].adapter_fd=-1;					// don't change this
	lights[1].lasterr=0;						// don't change this
	bzero(&lights[1].buf, sizeof(lights[1].buf));			// don't change this
	
	// THIRD SENSOR --> TSL2561_ADDR_HIGH
	lights[2].adapter_nr=1;						// change this according to your i2c bus
	lights[2].sensor_addr=TSL2561_ADDR_HIGH;			// don't change this
	lights[2].integration_time=TSL2561_INTEGRATIONTIME_402MS;	// don't change this
	lights[2].gain=TSL2561_GAIN_16X;				// don't change this
	lights[2].adapter_fd=-1;					// don't change this
	lights[2].lasterr=0;						// don't change this
	bzero(&lights[2].buf, sizeof(lights[2].buf));			// don't change this
	
	// initialize the sensors
	for(i=0; i<3; i++) {
		rc = TSL2561_OPEN(&lights[i]);
		if(rc != 0) {
			fprintf(stderr, "Error initializing TSL2561 sensor %i (%s). Check your i2c bus (es. i2cdetect)\n", i+1, strerror(lights[i].lasterr));
			return 1;
		}
		// set the gain to 1X (it can be TSL2561_GAIN_1X or TSL2561_GAIN_16X)
		// use 16X gain to get more precision in dark ambients, or enable auto gain below
		rc = TSL2561_SETGAIN(&lights[i], TSL2561_GAIN_1X);
		
		// set the integration time 
		// (TSL2561_INTEGRATIONTIME_402MS or TSL2561_INTEGRATIONTIME_101MS or TSL2561_INTEGRATIONTIME_13MS)
		// TSL2561_INTEGRATIONTIME_402MS is slower but more precise, TSL2561_INTEGRATIONTIME_13MS is very fast but not so precise
		rc = TSL2561_SETINTEGRATIONTIME(&lights[i], TSL2561_INTEGRATIONTIME_101MS);
	}
	
	// you can now sense each sensor when you like
	for(i=0; i<3; i++) {
		// sense the luminosity from the sensors (lux is the luminosity taken in "lux" measure units)
		// the last parameter can be 1 to enable library auto gain, or 0 to disable it
		rc = TSL2561_SENSELIGHT(&lights[i], &broadband, &ir, &lux, 1);
		printf("Test sensor %i. RC: %i(%s), broadband: %i, ir: %i, lux: %i\n", i+1, rc, strerror(lights[i].lasterr), broadband, ir, lux);
	}
	
	// when you have finisched, you can close things
	for(i=0; i<3; i++) {
		TSL2561_CLOSE(&lights[i]);
	}
	
	return 0;
}

Reader's Comments

  1.    

    Hallo,

    can we use this header file for TSL45315 light sensor too??

    Regards

    Reply to this comment
    •    

      Hi there.
      I’m very sorry, but I don’t know if it will going to work with TSL45315. Sincerely I don’t think so.

      Reply to this comment
      •    

        hallo,

        No problem, let me try it

        Reply to this comment
      •    

        Hallo,

        as you mentioned , it doesnt work!!! i am planning to write a library for TSL45315. could you please give some suggestion about the process?
        i am totally new to these thigs?

        Thanks a lot !

        Reply to this comment
        •    

          I would begin reading the python driver from WebIOPi (once downloaded go to: WebIOPi-0.7.1/python/webiopi/devices/sensor/tslXXXX.py)
          You can check the classes “TSL45315” and “TSL4531” where the driver is implemented.

          It seems to be quite similar to TSL2561, but the capture times are different:
          VAL_TIME_400_MS = 0x00
          VAL_TIME_200_MS = 0x01
          VAL_TIME_100_MS = 0x02

          Reply to this comment
          •    

            Thanks for tips..
            I like to code in c and using wiringpi…
            Thanks for the tips.i started already!

          •    

            My pleasure!

          •    

            By the way i forgot to mention that it worked by changing capture times…So others can do the same..

  2.    

    It works great also on Arietta G25… of course.
    Thanks very much for sharing it.

    Reply to this comment
  3.    

    I used this library in my project, thank you! One question… With the sensor looking out the window and a light on, turning the light off causes the lux to increase. Can you explain this? Thanks again for the great library.

    Reply to this comment
    •    

      Hi Anon. I don’t know what it could be.
      Already tried with TSL2561_INTEGRATIONTIME_402MS (that is more precise) and gain set to TSL2561_GAIN_16X (more precise in dark ambients, autogain disabled) ?

      Reply to this comment
  4.    

    Hi,

    I try to use the tsl2561 with TWI(i2C)in avrstudio. But i’m totaly lost and the compiling from your code say ”TXEN” & ”UDRE” undeclared . That’s normal ? Can I use your code in AVR ?
    Thanks.

    Reply to this comment
  5.    

    Thank you for this.
    I would like to use 3 light sensors. I can find each at 29, 39, and 49. I can find them using “sudo i2cdetect -y 1″. I see you defaulted to thee 39 address or “Float”. I am having problems opening a second sensor at the same time. should I open one, intilize, read, close, then Open, Intilialize, read, close the second?
    Would it be possable to have 2 or 3 open at the same time?

    Reply to this comment
    •    

      Yes, it’s possibile to use them in parallel.

      Just do the same you already did with the selectors TSL2561_ADDR_LOW, TSL2561_ADDR_FLOAT and TSL2561_ADDR_HIGH.

      Now I’m in bed, writing with my google nexus 4, and it’s 04.24 AM. Time to sleep. I’ll show you how to do tomorrow.
      Bye.

      Reply to this comment
    •    

      Please check the last example I’ve just added to the post.

      Let me know if it works for you, I don’t have 3 sensors to try it, but it should work.

      Bye!! Dino.

      Reply to this comment
  6.    

    Thanks for this code. I used this in my project nettemp.pl https://github.com/sosprz/nettemp. For now beta wersion.

    thanks to you I’ve added support for sensor TSL2561 😀

    Reply to this comment
  7.    

    Hi,

    Thank you so much. Initially when I compiled the program it showed an error message to check with i2c bus.

    But when I tried with “sudo i2cdetect -y 1” it could actually detect and also checked with address and everything seemed perfect.

    So the problem was not installing “wiringpi library” So by installing and including this library i could check with gpio pins which was enabled and showed status high (with i2c pins)

    So now the program is working after installing the library “wiringPi”

    Thank you so much 🙂

    Reply to this comment
  8.    

    This is great! Exactly what I was looking for. By the way, it seems to work out of the box on the Beaglebone Black.

    Please consider posting this code on github to make it more widely available.

    Thanks very much for sharing it.

    Reply to this comment

Lascia un commento

*