will be read-only starting December 31st, 2018. For more info pleae look at this Forum Post

TSL235R Light to Frequency sensor.

Last Modified: November 30, 2013, at 05:05 AM
By: robtillaart
Platform: UNO (others not tested)

remarks & comments


The Arduino is often used for reading sensors and this short article describes a few experiences I have done with the TSL235R sensor.

- - (datasheet)

The sensor is very straightforward, it has three pins:

  • PIN 1 = GND
  • PIN 2 = 5V
  • PIN 3 = Signal

The signal is a 50% square wave from which the frequency is linear with the amount of energy uW/cm^2. According to figure 1 of the datasheet there is a constant factor (~1.2) between these two at specific wavelength. As I only want to get a first order estimation of the energy coming in for now, I will use just a 1 to 1 ratio ==> so 1 Khz == 1uW/cm^2 == 10mW/m^2. If you need better accuracy, see improvements below.

When writing this article, it was a clouded day (looked like rain) and I got 20.000-25.000 IRQ's per second. PUtting on the lights the nr of IRQ's rose to 110.000 - 160.000 ! That's counting!

TSL235R sketch

A small sketch shows how the sensor can be used.

 *    FILE: demo01.pde
 *  AUTHOR: Rob Tillaart
 *    DATE: 2011 05 16
 * PURPOSE: prototype TSL235R monitoring  
 * Digital Pin layout ARDUINO
 * =============================
 *  2     IRQ 0    - to TSL235R
 * PIN 1 - GND
 * PIN 2 - VDD - 5V

volatile unsigned long cnt = 0;
unsigned long oldcnt = 0;
unsigned long t = 0;
unsigned long last;

void irq1()

void setup() 
  pinMode(2, INPUT);
  digitalWrite(2, HIGH);
  attachInterrupt(0, irq1, RISING);

void loop() 
  if (millis() - last >= 1000)
    last = millis();
    t = cnt;
    unsigned long hz = t - oldcnt;
    Serial.print("FREQ: "); 
    Serial.print("\t = "); 
    Serial.print((hz+50)/100);  // +50 == rounding last digit
    Serial.println(" mW/m2");
    oldcnt = t;

In setup() the IRQ routine that counts the pulses is initialized, it uses an unsigned long counter cnt that is increased at every RISING edge. This counter is never reset so it will eventually overflow. As the light energy differs over the day it is hard to say how long it takes before overflow occurs.

In loop() we wait for 1000 millis before printing the pulses of the last second. The frequency is converted to mW/m^2, note we added 50 to the frequency before dividing by 100 to round off the number. For speed reasons I kept the math in the integer domain.


The conversion of freq to mW/m2 can be adjusted for the small factor in figure 1 of datatsheet. I estimate this factor to 1.584 (this magical number is 10^0.2, as I estimate the delta ~0.2.

For very low radiation levels (=frequencies) one could do the following trick. Instead of only use the RISING edge, one could trigger the IRQ on a CHANGE. The number of IRQ's double and the math should of course handle that. Be aware this should only be done at low radiation levels as otherwise the number of IRQ's easily overflow the main application. Mind you we have ~150.000 IRQ's with some lights on, so doubling that would give up to 300K or more IRQ's.

In some articles on the web the math is adjusted for the internal size of the sensor [AFAIK these are about the TSL230 sensor]. I don't know if this must be done for the TSL235 too. I assume the builders of the TSL235R have build it in otherwise the figures in the datasheet do not make sense.

A better formula can be made to compensate for temperature and voltage (a.o).


Delay() is not used for timing as it is not reliable when many IRQ's occur, and they do. [you might test this]

Enjoy tinkering,