Using a photoresistor to measure RPM of a fan with 3 blades

I am trying to use a led and photoresistor to measure the rpm of a fan with 3 blades attached to 3-6 v dc motor. I have everything working and getting the photoresistor to output according. However, I do not know how to code this to get the RPM. Currently I have tried an if statement, when the photoresistor passes a value, to calculate the period. However, with the way Millis works, it is not a correct Period and that does not account for speeding up or slowing down the fan blades. I am not asking for the answer but any direction to go from here would be much appreciated.

What I am trying to do with this code, is once the photoresistor breaks 300, I find the period which I used to find freq. and then RPM.The period is just the start of the program – 0, their forward, is just the new time of the program – the old timestamp when it found period last. However, period is just becoming time blocks in between resetting the fan blade. With that being said, the RPM always reads zero. The whole idea I had with this code feels wrong, but I am not sure where to turn to next.


const int analogInPin = A1;  
const int analogOutPin = 6; 
int photoR = A0;  
int oldPR=0;
int curPR =0;
float p = 0;
float RPM;
float timelate =0;
float starttime =0;
int sensorValue = 0;        
int outputValue = 0;       
int f=0;


void setup() {
  
  Serial.begin(9600);
  pinMode( photoR,INPUT);

}

void loop() {
 
  sensorValue = analogRead(analogInPin);
  outputValue = map(sensorValue, 0, 1023, 0, 255);
  analogWrite(analogOutPin, outputValue);
  curPR = analogRead(photoR);
 
 

  
   if (curPR > 300 && oldPR <300  ){
    
   starttime = millis();
    p = (starttime - timelate) ;
    timelate = starttime;
   
    
    }
    
    oldPR = curPR;
    f = ((1/p)*1000);
    RPM = (f*60)/2 ;
    
   
     
  
  

  Serial.print("\t sensor = ");
  Serial.print(sensorValue);
  Serial.print("\t output = ");
  Serial.println(outputValue);
  Serial.print(" photoR = ");
  Serial.print (analogRead(photoR));
  Serial.print("\t RPM = ");
  Serial.print (RPM);


  delay(500);
}

Welcome to the forum!

Help us help you: please read the "How to get the best out of the forum" post, and post your code, using code tags. Explain what it should do, and what it does instead.

Note that photoresistors are very slow, and not suitable for measuring high RPMs by optical tecnniques. Most people use a photodiode.

I did update my post, thank you for the input.

LDR is quite slow compared to a phototransistor (both low cost’)

You could use a pin-change interrupt to tick off each blade passing.
Use millis() or micros() to determine the interval, divide by the number of fan blades, and carry on from there.

Thank you for the input, I have to use a photoresister. I'll look more into ISR.

It may not be reliable at higher fan speeds, maybe you can find one edge per revolution, rather than detecting multiple blades

Are you running the tests with NO light, except the LED?

Yes, beside my compture screen which I have my book actting as a screen to reflect as much light from that as I can.

Please explain this limitation. How fast is the fan rotating?

Good, gives a better signal that way. And are the fan blades metal or are they plastic that let some light through?

Plastic and came as see through but I have sprayed painted them black with 2 coats.

I can contorl the speed of the fan with a peoteometer, however, I can not give you a a speed but zero when it is off. Limiatation, this is a homework assignment and this is what was asked of us.

I tried the ISR could not get the if statement inside the ISR to work .

If i put Timeold = millis() inside the ISR it show that both TIme and Timeold equal each other.

when I add the great then and then the equals equals statements Timeold just = zero.

I have never used a ISR before but I am using pin A0 and I think I have it turn on correct.

const int analogInPin = A1;  
const int analogOutPin = 6; 
int photoR = A0;  
int oldPR=0;
int curPR =0;
float p = 0;
float RPM;
int sensorValue = 0;        
int outputValue = 0;       
int f=0;
unsigned long Time=0;
unsigned long Timeold=0;
int a=0;
void setup() {
  
  Serial.begin(115200);
  pinMode( photoR,INPUT);
  PCICR |= B00000010;
  PCMSK1 |= B00000001;
}


ISR (PCINT1_vect){

  if ( curPR >oldPR){
   return;
   
    }
    else if ( curPR == oldPR){
      
      return;
      
      }
      else{
    Timeold=millis();
      }
  
}
    
    
  



void loop() {
 
  sensorValue = analogRead(analogInPin);
  outputValue = map(sensorValue, 0, 1023, 0, 255);
  analogWrite(analogOutPin, outputValue);
  curPR = analogRead(photoR);
  p=(Time-Timeold)/3;

  

    cli();
    Time = millis();
    p=(Time-Timeold)/3;
    oldPR = curPR;
    f = ((1/p)*1000);
    RPM = (f*60)/2 ;
    sei();
     
  
  

  Serial.print("\t sensor = ");
  Serial.print(sensorValue);
  Serial.print("\t output = ");
  Serial.print(outputValue);
  Serial.print(" photoR = ");
  Serial.print (analogRead(photoR));
  Serial.print("\t RPM = ");
  Serial.print(RPM);
  Serial.print(" timeold = ");
  Serial.print(Timeold);
  Serial.print(" time= ");
  Serial.println (Time);


  delay(500);
}

What are you using analogWrite() for?
For your original approach, it is not wrong to set a threshold and watch it being crossed in an upwards direction and keeping a timestamp when it crosses. The risk is that you get fluctuating or spurious readings so you may have to add some filtering/averaging.
I don't think the output of a photo resistor is suitable for triggering an interrupt unless you use say the built-in comparator available on a Uno, which may go beyond the scope of this assignment.
You can't mix millis() and delay() as you have done in your first code sample.

Edit

In your original code you should calculate the RPM and do the printing inside this if statement instead of every loop() iteration:
if (curPR > 300 && oldPR <300 ){ }

With analogRead() values near 300, @dangmidnight will not be seeing a high enough voltage to trip the pin change interrupt.

@dangmidnight
Here's a very good tutorial on the analog comparator.

There are plenty of other tutorials on line as well.

A photo-resistor is really bad suited for this application.

If your teacher is not the most selfish-arrogant type of human beeing that is unable to say
"sorry my bad"

The first step of your work should be to measure how fast does the photoresistor react on the change from light ON to LIGHT OFF.

This is done with a code that switches ON the LED wait for 10 seconds then
switch off LED and store a time-stamp
then reading in the value of the photo-resistor in a fast running loop
always checking if value has changed over a threshold-value and if the value has changed
store a second timestamp into a second variable
Then you can calculate the difference to see how long it takes for the photo-resistor to change its value

By varying the physical conditions

  • how much ambient light
  • resistance-value of voltage-divider
  • distance between LED and photo-resistor

you will get a variaty of times it takes for the photo-resistor to change its value enough to be able to detect this as "light-beam interrupted"

I'm pretty sure that the maximum rpm that can be measured by a photo-resistor will be pretty low

best regards Stefan

I believe that this is an excellent exercise in specifying sub optimal components in order to test the student's ability to come up with a creative solution.

As many others have said a photoresistor is totally unsuitable for this application.
1: its not directional - so it will pick up ambient light
2: its SLOW

Also its a bad assignment because its "closed; it constrains you to doing something in a particular way; when really you should be asked to measure the speed of a fan, consider (and maybe research) alternative ways to do it, choose one, justify your choice, and implement the chosen solution.

Given the constaints your first step should be to read the LDR
1: with the fan blade blocking the light from the LED, then
2:m with the fan blade moved so the LDR is exposed to the LED.

If you dont see much difference you will need to think again.

The difference in voltage-drop will depend on

  • level of ambient light
  • distance between LED and photo-resistor
  • circuitry used (simple voltage-divider, external analog chip (comparator like LM339)

@dangmidnight
not sure how much you know about electronic components and their names
please post a picture of your photo-resistor maybe it IS a photo-transistor

You're not actually counting the number of times the LDR is changing, you're using delay(500) so your code is checking the status of the LDR every 500 milliseconds.
edit: in fact, you're not even using the status of the pin, just calculating the frequency which the loop runs at. With a 500 millisecond delay each time it'll probably always say the rpm is around 180?

You need to continue looping without delay and count all the pulses then after a certain number of counts you could work out the total time and calculate the frequency from that.