Light Sensor Response Time and Choosing a Light Sensor

I am working on a project that involves communicating with LEDs. I need to be able to detect the state of a light source that is flashing at about 100 Hz. I am using the phototransistor that comes with the Arduino Starter Kit, but I am noticing that, while the phototransistor detects a difference between the high state and low state, this is very minimal (and about 200 values higher than the phototransistor's value when the LED is completely off). I find this very confusing because the datasheet for the phototransistor claims that the reaction time is 2 microseconds, while my project is working on the scale of milliseconds.

What could be causing this? Are there any other components (preferably inexpensive) that I can use to detect light from a source that is flashing at this frequency? It would be amazing if the light sensor could yield a value when the LED is low that is the same as if the LED was never on. Any help would be very much appreciated. Thank you!

The datasheet for the photoresistor can be found here:

Thank you for the response! I understand what you mean. However, I have tested the phototransistor in the same environment without the LED and the value is around, 50. If I have the LED on, it is around 500. However, when flickering, the value will fluctuate from 350-500. I was wondering if there was anyway to achieve a fluctuation of 50-500. I have noticed that decreasing the frequency helps, but it is necessary that I maintain a frequency of at least 100 Hz. Are there any other forms of light detection that would help with this?

Sorry about that! I am new to the forums, and I will try to be more descriptive in the future. But, if anyone could answer this question, I would be very grateful!

Phototransistors are MUCH faster than the Arduino, especially if you print each sample, which is a very slow operation.

Post your code, using code tags ("</>" button).

#include <SPI.h>
#include <TimerOne.h>

const int CHIP_SELECT = 12;
const int LED_PIN = 3;
const int PHOTO_PIN = 0;

const int RESISTOR_VALUE = 255;

boolean triggered = false;
boolean data = false;

void setup()
{
  pinMode(LED_PIN, OUTPUT);
  pinMode(PHOTO_PIN, INPUT);
  
  setupPot();
  
  Timer1.initialize(3000);
  Timer1.attachInterrupt(trigger);
  
  Serial.begin(115200);
}

void setupPot()
{
  pinMode(CHIP_SELECT, OUTPUT);
  
  SPI.begin();
  
  digitalWrite(CHIP_SELECT, LOW);
  SPI.transfer(B00010001);
  SPI.transfer(RESISTOR_VALUE);
  digitalWrite(CHIP_SELECT, HIGH);
}

void loop()
{
  if(triggered)
  {
    digitalWrite(LED_PIN, data);
    delayMicroseconds(1500);
    Serial.println(analogRead(PHOTO_PIN));
    
    data = !data;
    
    triggered = false;
  }
}

void trigger()
{
  triggered = true;
}

Thank you for the response! Above is the code that I was running when I ran into the aforementioned problems (the frequency I was using was a little more than 100 Hz). For testing purposes, I had the transmitter and receiver running on the same Arduino. The SPI code should not be relevant to the problem (SPI was used to control a digital potentiometer). If the phototransistor is much faster than the Arduino, shouldn't it be able to detect the darkness fully?

Post a circuit diagram (hand drawn, not Fritzing).

Eliminate stray light by enclosing the photodiode and LED in a black tube (making sure that they are pointed directly at each other), with the ends taped shut with black tape.

For testing, get rid of ALL extraneous code.

I have attached a picture of the circuit, and I have included new, simplified code (it does not appear as if simplifying the code had an effect).

#include <SPI.h>
#include <TimerOne.h>

const int CHIP_SELECT = 12;
const int LED_PIN = 3;
const int PHOTO_PIN = 0;

const int RESISTOR_VALUE = 255;

boolean triggered = false;
boolean data = false;

void setup()
{
  pinMode(LED_PIN, OUTPUT);
  pinMode(PHOTO_PIN, INPUT);
  
  setupPot();
  
  Timer1.initialize(3000);
  Timer1.attachInterrupt(trigger);
  
  Serial.begin(115200);
}

void setupPot()
{
  pinMode(CHIP_SELECT, OUTPUT);
  
  SPI.begin();
  
  digitalWrite(CHIP_SELECT, LOW);
  SPI.transfer(B00010001);
  SPI.transfer(RESISTOR_VALUE);
  digitalWrite(CHIP_SELECT, HIGH);
}

void loop()
{
  if(triggered)
  {
    digitalWrite(LED_PIN, data);
    
    delayMicroseconds(1500);
    Serial.println(analogRead(PHOTO_PIN));
    
    data = !data;
    
    triggered = false;
  }
}

void trigger()
{
  triggered = true;
}

Sorry, I uploaded the wrong code. The simplified code is shown below. I've done more testing and it seems to work at 10 Hz, but not at 20 Hz.

const int LED_PIN = 3;
const int PHOTO_PIN = 0;
boolean data = false;

void setup()
{
  pinMode(LED_PIN, OUTPUT);
  pinMode(PHOTO_PIN, INPUT);
  
  Serial.begin(115200);
}

void loop()
{
    digitalWrite(LED_PIN, data);
    
    delay(25);
    Serial.println(analogRead(PHOTO_PIN));
    data = !data;
    delay(25);
}
void loop()
{
    digitalWrite(LED_PIN, data);
    
    delay(25);
    Serial.println(analogRead(PHOTO_PIN));
    data = !data;
    delay(25);
}

You read the detector only so often while varying the led level every how often?

The phototransistor may read in 2 usecs but analog read takes 105 usecs.

See about reading in every loop() without the delays. Save the highest and lowest and print those every 250 msecs.

Thank you for the response! Unfortunately, even with the maximum and minimum, the problem persists.

void loop()

{
   digitalWrite(LED_PIN, data);
   
   delay(25);
   Serial.println(analogRead(PHOTO_PIN));
   data = !data;
   delay(25);
}

Have you heard of the Nyquist criterion? To sample a specific frequency and resolve that frequency unambiguously (so it doesn't look like any other frequency) you must sample at at least twice the frequency.

You are sampling at less than 20Hz with that code. So of course it works at 10Hz and not at 20Hz.

Now, if you can synchronise to the input, then you can choose to sample 20Hz at 20Hz but then you're not actually resolving a frequency but getting digital bits at defined points in time. That's why SPI works so much faster than I2C. SPI has a separate 'clock' wire which tells the receiver exactly when it should look at the data wire to observe a high or low bit.

Thank you for the response! I am just a little confused. In the code that I provided, why is it not sampling at 20 Hz (isn't the period of the sampling 50 ms)? Additionally, when I said that it worked when sampling at 10 Hz, I meant that if I changed the delay(25)s to delay(50)s, then I was able to achieve the desired result (sorry if that was ambiguous). If the problem is with regards to the Nyquist criterion, would that cause the undesired results consistently?

Try this experiment: in setup() turn the LED on, and in loop() record a bunch of samples from the phototransistor as fast as you can. Examine the results for increase/decrease in the ADC values.

Repeat, leaving the LED off. Post a synopsis of the results.

It's not sampling at 20Hz because you have 50ms of delay PLUS the processing time of all the other stuff you are doing. That time will be small, but the drift from 20Hz is probably more than 10%.

If you must do things on a good time schedule that doesn't drift and is accurate to better than a millisecond, use the Blink-Without-Delay technique. It's in your Examples folder, under 02Digital.

If you need timing to the microsecond level of precision, then a hardware timer can be used, but that's an advanced topic.

dts182:
Thank you for the response! Unfortunately, even with the maximum and minimum, the problem persists.

May I see the code you use? It should be reading at least 5Khz. Isn't your signal only 100Hz? 50x slower? If you do 8-bit analog reads instead of 10-bit, you can run a lot faster.

Also IIRC with AVR ADC you can adjust the sensitivity by providing a reference voltage and again IIRC there's an on-chip magnifier but that may not be all AVR's. I'll haveta look.

Edit:

328P datasheet
The ADC converts an analog input voltage to a 10-bit digital value through successive approximation. The minimum
value represents GND and the maximum value represents the voltage on the AREF pin minus 1 LSB.
Optionally, AVCC or an internal 1.1V reference voltage may be connected to the AREF pin by writing to the REFSn
bits in the ADMUX Register. The internal voltage reference may thus be decoupled by an external capacitor at the
AREF pin to improve noise immunity.

If you still read 200, the internal 1.1V will make that near 900.

One other thing. Roll up a strip of paper to make a tube 2 or 3 cm long, adjust to fit the phototransistor bulb and tape. Put the detector into the tube and point it right at the led. Now the background light is less by far. The longer the tube, the narrower the view. It is the "beam", not the light. If you do this with the lights it will make it harder get both pointing exact than just one first then turn the other. On the detector, that is how to IR trip wire even in daytime. Blocked is blocked, illuminate a wall with IR and point "beam detectors" at it.

Interestingly, this thread had spawned another discussion (on StackExchange): What's causing a bad response time of a phototransistor at 100 Hz ?