![]() |
Forum Index : Microcontroller and PC projects : uM2(+): TLS2561 lux measurement
Author | Message | ||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 8648 |
This code was written at the request of Lew247 who supplied me with the TLS2561 module. This is a 6-pin chip with an I2C interface that measures light levels in both the visible and IR spectrums and then uses the IR measurement to correct the visible measurement. The program uses the default measurement rate of every 402msec and uses an interrupt to trigger the calculation after each conversion. The chip has programmable gain settings of 1 and 16 which coupled with a measurement range of 16-bits gives a 1:1000000 dynamic range. The program autromatically switches between the gain ranges to optimise the measurement The program calculates the light falling on the chip in LUX (one LUX == one lumen per square metre) Lew: I don't know if this chip will do what you want. It has a maximum measurement of about 0.0304 * 65535 (assuming no IR, lower in the presence of IR) which gives maximum 1992 lux whereas outdoor average sunlight ranges from 32000 to 100000 lux. You may be able to compensate for this with a neutral density filter but this gets way outside my levels of knowledge of light. The chip is clearly intended primarily for indoor uses option explicit option default none const intpin = 12 'connect to the INT pin on the TLS2561 const TLSaddr=&B0111001 'Floating, GND is &B0101001, VCC is &B1001001 const TLScontrol = &H00 const TLStiming = &H01 const TLSINTERRUPT = &H06 const TLSCRC = &H08 const TLSID = &H0A const TLSDATA0LOW = &H0C const TLSDATA0HIGH = &H0D const TLSDATA1LOW = &H0E const TLSDATA1HIGH = &H0F const TLSCMD = &H80 const TLSCLEAR = &H40 const TLSPOWER = &H03 const TLSLOWGAIN = &H00 const TLSHIGHGAIN = &H10 CONST TLSINTEG402 = &H02 dim integer C1datalow,C1datahigh,C0datalow,C0datahigh,useresult=1 dim float CH1,CH0,lux,gain=1.0 i2c open 400,1000 'device will run at full speed i2c write TLSaddr,0,2,TLSCMD OR TLScontrol,TLSPOWER ' turn on the device i2c write TLSaddr,0,2,TLSCMD OR TLStiming,TLSINTEG402 OR TLSLOWGAIN 'set output every 402msec, gain = 1 setpin intpin,intl,TLS2561int,PULLUP i2c write TLSaddr,0,2,TLSCMD OR TLSCLEAR OR TLSINTERRUPT,&H10 ' enable interrupts every ADC conversion ' do pause 1000 loop ' sub TLS2561int i2c write TLSaddr,1,1,TLSCMD OR TLSDATA0LOW i2c read TLSaddr,0,1,C0datalow i2c write TLSaddr,1,1,TLSCMD OR TLSDATA0HIGH i2c read TLSaddr,0,1,C0datahigh CH0=(C0datahigh*256 + C0datalow)/gain i2c write TLSaddr,1,1,TLSCMD OR TLSDATA1LOW i2c read TLSaddr,0,1,C1datalow i2c write TLSaddr,1,1,TLSCMD OR TLSDATA1HIGH i2c read TLSaddr,0,1,C1datahigh CH1=(C1datahigh*256 + C1datalow)/gain i2c write TLSaddr,0,2,TLSCMD OR TLSCLEAR OR TLScontrol,TLSPOWER 'clear the interrupt if CH0*gain < 500 and gain = 1.0 then 'switch to low light i2c write TLSaddr,0,2,TLSCMD OR TLStiming,TLSINTEG402 OR TLSHIGHGAIN 'switch to high gain useresult=0 gain=16.0 Print "Switching to high gain" elseif CH0*gain > 10000 and gain = 16.0 then 'switch to normal light i2c write TLSaddr,0,2,TLSCMD OR TLStiming,TLSINTEG402 OR TLSLOWGAIN 'switch to high gain useresult=0 gain=1.0 Print "Switching to low gain" else select case CH1/CH0 'normal processing case IS > 0,<=0.5 lux = 0.0304 * CH0 - 0.062 * CH1 * ((CH1/CH0)^1/4) case IS >0.5,<=0.61 lux = 0.0224 * CH0 - 0.031 * CH1 case IS >0.61, <=0.8 lux = 0.0128 * CH0 - 0.0153 * CH1 case IS >0.8, <=1.3 lux = 0.00146 * CH0 - 0.00112 * CH1 case else lux = 0 end select if useresult then 'skip the first result after gain change print "ADC count visible + IR=",CH0; print " ADC count IR only=",CH1; print " converts to ",lux," Lux" else useresult=1 'enable for next time endif endif end sub |
||||
bigfix Senior Member ![]() Joined: 20/02/2014 Location: AustriaPosts: 124 |
Great - saves me a lot of time ! I was just going to add one to my shade control Minor Correction I assume it is the TSL2561 - not TLS2561 |
||||
lew247![]() Guru ![]() Joined: 23/12/2015 Location: United KingdomPosts: 1676 |
Thanks matherp It's just what I needed Out of curiousity do you think there's be an easy way to measure just the UV light? ie so it can show the uv index? or would I need to get a Si1145 as well? |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 8648 |
The TSL2561 just measures visible and IR. It has no capability of measuring UV |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 8648 |
HankR sent me a PM about this code and prompted me to have another look at the datasheet as the LUX limitations in my code don't seem to correspond to the Adafruit materials. I think I have a x16 scale problem but it is difficult to tell from the datasheet. The key equation is: lux = 0.0304 * CH0 - 0.062 * CH1 * ((CH1/CH0)^1/4) assuming the IR measurement is 0 then this can be simplified to: lux = 0.0304 * CH0 so the absolute limit on lux given a 16-bit ADC would be 65535 * 0.0304 = 1992 This is with gain = 1x However, reading the datasheet again and looking at the Adafruit code it appears they are treating the 16x gain mode as default and 1x gain as dividing the reading by 16 rather than the other way round as assumed in my code. Changing this makes the maximum lux reading: 65535 * 0.0304 / (1/16) = 26592 This makes more sense to the usability of the device so to make this change in the code just substitute these lines for the originals. NB this still won't deal with light levels seen in normal outdoor sunshine dim float CH1,CH0,lux,gain=0.0625
sub TLS2561int i2c write TLSaddr,1,1,TLSCMD OR TLSDATA0LOW i2c read TLSaddr,0,1,C0datalow i2c write TLSaddr,1,1,TLSCMD OR TLSDATA0HIGH i2c read TLSaddr,0,1,C0datahigh CH0=(C0datahigh*256 + C0datalow)/gain i2c write TLSaddr,1,1,TLSCMD OR TLSDATA1LOW i2c read TLSaddr,0,1,C1datalow i2c write TLSaddr,1,1,TLSCMD OR TLSDATA1HIGH i2c read TLSaddr,0,1,C1datahigh CH1=(C1datahigh*256 + C1datalow)/gain i2c write TLSaddr,0,2,TLSCMD OR TLSCLEAR OR TLScontrol,TLSPOWER 'clear the interrupt if CH0*gain < 500 and gain = 0.0625 then 'switch to low light i2c write TLSaddr,0,2,TLSCMD OR TLStiming,TLSINTEG402 OR TLSHIGHGAIN 'switch to high gain useresult=0 gain=1.0 Print "Switching to high gain" elseif CH0*gain > 10000 and gain = 1.0 then 'switch to normal light i2c write TLSaddr,0,2,TLSCMD OR TLStiming,TLSINTEG402 OR TLSLOWGAIN 'switch to high gain useresult=0 gain=0.0625 Print "Switching to low gain" else select case CH1/CH0 'normal processing case IS > 0,<=0.5 lux = 0.0304 * CH0 - 0.062 * CH1 * ((CH1/CH0)^1.4) case IS >0.5,<=0.61 lux = 0.0224 * CH0 - 0.031 * CH1 case IS >0.61, <=0.8 lux = 0.0128 * CH0 - 0.0153 * CH1 case IS >0.8, <=1.3 lux = 0.00146 * CH0 - 0.00112 * CH1 case else lux = 0 end select if useresult then 'skip the first result after gain change print "ADC count visible + IR=",CH0; print " ADC count IR only=",CH1; print " converts to ",lux," Lux" else useresult=1 'enable for next time endif endif end sub |
||||
lew247![]() Guru ![]() Joined: 23/12/2015 Location: United KingdomPosts: 1676 |
IF it helps, I found this Bascom code It "should" be relatively easy to modify for the Micromite but I don't have enough knowledge to do it [code]$ Sim $ Regfile = "m8def.dat '' for use microcontroller $ Crystal = 8000000 'clock $ Hwstack = 40 'Hardware Stack $ Swstack = 32 'SW Stack $ Frame size = 60 'Frame $ Baud = 9600 Waitms 250 Declare Sub POWER_UP 'enable TSL2561 Declare Sub Power_down 'disable TSL2561 Read Declare Sub GET_DATA 'data from TSL2561 (Read Word Protocol) Declare Sub Calclux 'Lux calculate values Send Declare Sub output 'values ??via UART Declare Sub Control_ack 'Check whether ACK has been received Declare Sub Lux_1 'first part Lux Calculation Declare Sub Lux_2 'second part Lux Calculation Declare Sub Lux_ges' Lux1 - Lux2 (see data sheet "Calculating lux") Led Alias ??Portd.5 'LED lights = TSL2561 powered up Config Led = Output Led = 0 Config = Sda Portc.4 Config Scl = Portc.5 Config I2cdelay = 10 I2cinit Dim Slave Address As Byte Dim Lux1 As Single Lux1 = 0 Dim Lux2 As Single Lux2 = 0 Dim Lux As Single Lux = 0 Dim Ctrl As String * 10 Dim C0low As Byte Dim C0high As Byte Dim C0 As Word Dim C1low As Byte Dim C1high As Byte Dim C1 As Word Dim C As Single Dim Y1 As Single Dim Y2 As Single '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ''70 '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' I2C bus scan and output slave addresses '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' Print "I2C slave" Print "Looking for ..." Wait 2 For slave address = 0 To 254 Step 1 'for all straight (Step2) / odd and even (Step1) addresses Send I2cstart 'start condition Send I2cwbyte slave address' Address If Err = 0 Then 'I2C slave found? (ACK received?) Print "Slave dec"; slave address Print "h"; Hex (slave address); "B"; Bin (slave address) Wait 2 End If Releasing I2cstop 'Bus Next Print "end scanning" Wait 2 '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ''70 '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' Main Loop: Calculate values, calculate and output ' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' do I2cinit power_up Waitms 50 get_data Waitms 50 Power_down Calclux output loop End Sub POWER_UP I2cstart I2cwbyte & H72 'device address Ctrl = "POWER_UP" Control_ack I2cwbyte & H80 I2cwbyte & H03 'Power up device I2cstop Led = 1 End Sub Sub Power_down I2cstart I2cwbyte & H72 'device address I2cwbyte & H80 I2cwbyte & H00 'Power down device I2cstop Led = 0 End Sub '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ''70 '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' 'Read data from the ADC register the sensor' '' '' (I2C Read Word Protocol) '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' Sub gET_DATA Waitms 420 'Integration Time max.402ms I2cstart I2cwbyte & H72 'device address Ctrl = "GET_DATA" Control_ack I2cwbyte & HAC I2cstop I2cstart I2cwbyte & H73 'device address + 1 (read) I2crbyte C0low, Ack I2crbyte C0high, Nack I2cstop I2cstart I2cwbyte & H72 'device address I2cwbyte & HAE I2cstop I2cstart I2cwbyte & H73 'device address + 1 (read) I2crbyte C1low, Ack I2crbyte C1high, Nack I2cstop C0 = Makeint (c0low, C0high) C1 = Makeint (c1low, C1high) End Sub '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ''70 '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' Calculate Lux value (see data sheet "Calculating lux") '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' Sub Calclux C = C1 / C0 If C> 0 And C <= 0.5 Then Y1 = 0.0304 Lux_1 Lux2 = C1 / C0 Lux2 = Lux2 ^ 1.4 Lux2 = Lux2 * C0 Lux2 = Lux2 * 0.062 Lux_ges Elseif C> 0.5 And C <= 0.61 Then Y1 = 0.0224 Lux_1 Y2 = 0.031 Lux_2 Lux_ges Elseif C> 0.61 And C <= 0.8 Then Y1 = 0.0128 Lux_1 Y2 = 0.0153 Lux_2 Lux_ges Elseif C> 0.8 And C <= 1.3 Then Y1 = 0.00146 Lux_1 Y2 = 0.00112 Lux_2 Lux_ges else Lux = 0 End If End Sub '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ''70 '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' Read ADC Channel0 / Channel1 values ??and calculated lux value '' '' Via UART Send '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' Sub output Print "C0 value"; C0 Print "C1 value"; C1 Print "Lux"; lux End Sub '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ''70 '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' Check whether an ACK from the slave came back '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' Sub Control_ack If Err = 0 Then Print Ctrl; "ACK" else Print Ctrl; "NACK" End If End Sub Sub Lux_1 Lux1 = Y1 * C0 End Sub Sub Lux_2 Lux2 = Y2 * C1 End Sub Sub Lux_ges Lux = Lux1 - Lux2 End Sub[/code] |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 8648 |
The code I posted works properly. The only question was whether the "default" scaling was x1 or x16. I think based on a further reading of the datasheet and Hank's comments that it is x16 hence the second version of the subroutine should be used. Lew - please also check the PM from me |
||||
lew247![]() Guru ![]() Joined: 23/12/2015 Location: United KingdomPosts: 1676 |
ah, after a third pm I found the problem I actually replied to you earlier today and also sent you a message 2 weeks ago [code]matherp has exceeded the maximum number of Private Messages they are allowed to receive[/code] |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 8648 |
That is bizarre, I was sure I cleared them out and they seem to have re-appeared. Anyway cleared again so there should be room now. |
||||
Phil23 Guru ![]() Joined: 27/03/2016 Location: AustraliaPosts: 1664 |
Following this with interest. I'm interested in measuring energy falling on my Solar water panels. As mentioned in this thread, I,m just using a garden light solar panel ATM, not the ideal choice, but it has been connected to a LCD panel meter for a few weeks now and it's surprising how close to the Real solar meter it's readings are. Just based on voltage alone without doing any power calculations. I have a few other light sensors I planned to try, but to day this solutions is working well. I also own a few high end photographic exposure meters, and can add that they all employ some sort of optical attenuation in their outdoor reading modes. Not so much a Neutral density filter (as in grey glass), more the density of the white dome over the sensor & it's thickness. Two of them are actually supplied with 2 domes. (German Gossen meters). This page is worth a read on light, Lux etc. Exposure Values Cheers. |
||||
lew247![]() Guru ![]() Joined: 23/12/2015 Location: United KingdomPosts: 1676 |
I've just seen the comment in red in Matherp's original post above I found this on the module - is it right or wrong? [quote]The TSL2561 luminosity sensor is an advanced digital light sensor, ideal for use in a wide range of light situations. Compared to low cost CdS cells, this sensor is more precise, allowing for exact lux calculations and can be configured for different gain/timing ranges to detect light ranges from up to 0.1 - 40,000+ Lux on the fly. The best part of this sensor is that it contains both infrared and full spectrum diodes! That means you can separately measure infrared, full-spectrum or human-visible light. Most sensors can only detect one or the other, which does not accurately represent what human eyes see (since we cannot perceive the IR light that is detected by most photo diodes)[/quote] 0.1 - 40,000+ Lux on the fly The best part of this sensor is that it contains both infrared and full spectrum diodes! That means you can separately measure infrared, full-spectrum or human-visible light. |
||||
![]() |