TSL235R inconsistent output

I've hooked up a TSR235R (light intensity to frequency converted) so that it's connected to an IQR pin and am calculating the frequency of the pulses and then converting this to a value of the intensity of the light shone upon it. My sketch is as follows:

int period = 100;
float area = 0.0092; // Sensing area of TSL25R device, in cm2

volatile unsigned long pulses = 0;
unsigned long lastPulseOutputTime;

void irq1()
{
  pulses++;
}

///////////////////////////////////////////////////////////////////
//
// SETUP
//
void setup() 
{
  Serial.begin(115200);
  Serial.println("START");
  pinMode(2, INPUT);
  digitalWrite(2, HIGH);
  attachInterrupt(0, irq1, RISING);
}

///////////////////////////////////////////////////////////////////
//
// MAIN LOOP
//
void loop() 
{
  unsigned long elapsedTimeSincePulseCheck = millis() - lastPulseOutputTime;
  if (elapsedTimeSincePulseCheck >= period)
  {
    lastPulseOutputTime = millis();
    
    unsigned long frequency = getFrequency(elapsedTimeSincePulseCheck); 
    long radiance = getRadiance(frequency);
  
    pulses = 0;
  }
}

unsigned long getFrequency(unsigned long timePeriod) {
  unsigned long hz = (pulses) * (1000 / timePeriod);
  return hz;
}

long getRadiance(unsigned long frequency) {
  long irradiance = frequency / area;  // Calculate Irradiance (uW/cm2)
  return (irradiance);
}

The output irradiance is in uW/cm2. The issue is that the output varies quite largely even when the light source upon it is constant, a graph of the changing calculated irradiance is as follows:

Any ideas what's causing this?

millis() isn't anything like accurate enough, use micros().

Also you must protect your code from the interrupt routine writing the variable
pulses. Its a 4 byte variable so the main-line code will read 4 bytes from the
memory and the interrupt routine could sneak in between those reads.

You read the value thus:

  noInterrupts () ;
  unsigned long copy = pulses ;
  interrupts () ;

The other issue you have is you zero the value of pulses in the main code.

Don't do this, its losing accuracy.

Just record the value of pulses that you copied and subtract it next time from
the copy to give an accurate count of the number of times the ISR ran.

The zero'ing shouldn't be much of an issue, and you won't have to worry about overflow then. Turning off interrupts to make your calculation is good though. If possible, just copy out the current value and continue on.

What is the expected frequency range of the pulses? millis() might or might not be enough depending on the frequencies you expect.

You might want to initialize your variables not in the loop, but instead do it before setup. You could run into scope issues otherwise if you try to call your values somewhere else.

I've updated my code to the following as per the suggestions:

unsigned long period = 100000;
float area = 0.0092; // Sensing area of TSL25R device, in cm2

volatile unsigned long pulses = 0;
unsigned long lastPulseCount = 0;
unsigned long lastPulseOutputTime = 0;

void irq1()
{
  pulses++;
}

///////////////////////////////////////////////////////////////////
//
// SETUP
//
void setup() 
{
  Serial.begin(115200);
  Serial.println("START");
  pinMode(2, INPUT);
  digitalWrite(2, HIGH);
  attachInterrupt(0, irq1, RISING);
}

///////////////////////////////////////////////////////////////////
//
// MAIN LOOP
//
void loop() 
{
  unsigned long elapsedTimeSincePulseCheck = micros() - lastPulseOutputTime;

  if (elapsedTimeSincePulseCheck >= period)
  {
    lastPulseOutputTime = micros();
    
    noInterrupts();
    unsigned long pulsesCopy = pulses;
    interrupts();
    unsigned long pulsesSince = pulsesCopy- lastPulseCount;
    
    unsigned long frequency = getFrequency(elapsedTimeSincePulseCheck, pulsesSince); 
    long radiance = getRadiance(frequency);
    
    Serial.println(frequency);
  
    lastPulseCount = pulsesCopy;
  }
}

unsigned long getFrequency(unsigned long timePeriod, unsigned long pulseCount) {
  unsigned long hz = (pulseCount) * (1000000 / timePeriod);
  return hz;
}

long getRadiance(unsigned long frequency) {
  long irradiance = frequency / area;  // Calculate Irradiance (uW/cm2)
  return (irradiance);
}

However the output is still showing the same issues if oddly worse now, the graph of frequency looks as follows:

With the raw data being:

START
2210
2200
1980
2210
1980
1989
1980
2210
1980
2210
1980
2210
1980
1989
1980
2210
2200
1989
2200
1980
1989
2200
2210
1980
1980
2210
1980
2210
1980
1989
1980
2200
1989
1980
2210
1980
1980
2210
1980
2210
2200
1980
1989
2200
1980
1980
2210
1980
2200
2210
1980
1989
1980
1980
2210
2200
2200
1980
2210
2200
2200
1989
1980
1980
2210
1980
2200
2210
1980
1980
2210
1980
1989
1980
1980
2210
1980
1980
2210
1980
1989
1980
2200
1989
1980
2210
1980
2200
2210
1980
1980
2200
1989
1980
1980
1989
1980
2200
2200
1980
1980
1980
2200
2210
1980
1980
1989
1980
2210
1980
1989
2200
1980
2210
1980
1980
2210
1980
1980
2210
1980
1980
2210
1980
1989
2200
2200
1980
1971
1980
1980
2200
1980
1980
2200
2200
2200
2200
1980
1980
2200
2200
1980
2200
2200
2200
2200
2210
1980
1980
2200
1989
2200
1989
2200
2200
2210
1980
1980
1980
1980
2210

Check your variable scope. You should not reinitialize values you want to save. In fact, I'd try to minimize initializing values.

Condensed my code to the following however the issue still remains:

unsigned long period = 100000;
float area = 0.0092; // Sensing area of TSL25R device, in cm2

volatile unsigned long pulses = 0;
unsigned long lastPulseCount = 0;
unsigned long lastPulseOutputTime = 0;

void irq1()
{
  pulses++;
}

///////////////////////////////////////////////////////////////////
//
// SETUP
//
void setup() 
{
  Serial.begin(115200);
  Serial.println("START");
  pinMode(2, INPUT);
  digitalWrite(2, HIGH);
  attachInterrupt(0, irq1, RISING);
}

///////////////////////////////////////////////////////////////////
//
// MAIN LOOP
//
void loop()
{
  unsigned long elapsedTimeSincePulseCheck = micros() - lastPulseOutputTime;

  if (elapsedTimeSincePulseCheck >= period)
  {
    lastPulseOutputTime = micros();
    
    noInterrupts();
    unsigned long pulsesCopy = pulses;
    interrupts();
    
    unsigned long frequency = (pulsesCopy- lastPulseCount) * (1000000 / elapsedTimeSincePulseCheck);
    long irradiance = frequency / area;  // Calculate Irradiance (uW/cm2)
    
    Serial.println(frequency);
  
    lastPulseCount = pulsesCopy;
  }
}

Are you taking into account micros() overflow? It does not look like you are.

The documentation (http://arduino.cc/en/Reference/Micros) says:

This number will overflow (go back to zero), after approximately 70 minutes.

The program has not been running for 70 minutes so for now I'm not taking it into account no as it shouldn't make a difference I believe.

Something else to try (Though it might not do anything):
Save your variables to an array, and wait to print after you are done collecting data. Printing to the serial is relatively slow and might interfere with the pulse counts.

Oh and one other question we should have asked far earlier. Have you checked your signal with an oscilloscope and made sure there was nothing strange about it?

I followed as you advised and have saved the pulse counts to an array then output all the data after there are 100 counts in the array, code being as follows:

unsigned long period = 100000;
float area = 0.0092; // Sensing area of TSL25R device, in cm2

volatile unsigned long pulses = 0;
unsigned long lastPulseCount = 0;
unsigned long lastPulseOutputTime = 0;
unsigned long minVal = 0;

#define PULSE_ARRAY_SIZE 100
unsigned long pulseCounts[PULSE_ARRAY_SIZE];
int pulseArrayIdx = 0;

void irq1()
{
  pulses++;
}

///////////////////////////////////////////////////////////////////
//
// SETUP
//
void setup() 
{
  Serial.begin(115200);
  Serial.println("START");
  pinMode(2, INPUT);
  digitalWrite(2, HIGH);
  attachInterrupt(0, irq1, RISING);
}

///////////////////////////////////////////////////////////////////
//
// MAIN LOOP
//
void loop()
{
  unsigned long elapsedTimeSincePulseCheck = micros() - lastPulseOutputTime;

  if (elapsedTimeSincePulseCheck >= period)
  {
    lastPulseOutputTime = micros();
    
    noInterrupts();
    unsigned long pulsesCopy = pulses;
    interrupts();
    
    unsigned long frequency = (pulsesCopy- lastPulseCount) * (1000000 / period);
    long irradiance = frequency / area;  // Calculate Irradiance (uW/cm2)
    
//    Serial.println((pulsesCopy- lastPulseCount));

    if (pulseArrayIdx == PULSE_ARRAY_SIZE) {
      Serial.println("Done, results:");
      for (int i = 0; i < PULSE_ARRAY_SIZE; i++) {
        Serial.println(pulseCounts[i]);
      }
      pulseArrayIdx = -1;
    } else if (pulseArrayIdx != -1) {
      pulseCounts[pulseArrayIdx] = pulsesCopy- lastPulseCount;
      pulseArrayIdx++;
    }
    
    lastPulseCount = pulsesCopy;
  }
}

The output is odd as the pulse count varies despite the light source not changing, the graph of which is as follows:

And the raw data:

36
37
37
37
37
37
37
37
37
37
38
37
37
38
38
37
38
38
38
38
38
38
38
39
38
39
38
39
39
39
39
39
39
40
39
40
40
39
40
40
41
40
40
41
41
40
41
41
40
41
41
41
41
41
41
42
41
41
41
41
41
41
41
41
41
41
41
40
41
41
41
40
41
40
41
41
40
40
41
40
40
41
40
40
40
41
40
40
40
40
40
40
40
40
40
40
40
40
40
40

I have not yet checked the signal with an oscilloscope but will do ASAP. Any ideas what the cause of this issue could be? Is it something in the code or more likely to be hardware related?

Can't get hold of an oscilloscope at the moment unfortunately.