Help with tachometer using an IR Phototransistor and IR LED

Hi, I'm trying to use Arduino in order to calculate RPM. I have at my disposal an IR phototransistor and IR LEDs.

The components are the following:
IR phototransistor: 3mm,SFH 309 FA-3/4 with 10k ohm resistor
IR LED: 5mm 870nm 10deg with 220 ohm resistor

The idea was to have a small reflector on the edge of the wheel, and to point both the IR LED and IR Phototransistor at the edge of the wheel. Every time the wheel does a revolution, the LED shines into the sensor, pretty self-explanatory.

I have tried many many different circuit configurations. It still doesn't work. I don't know if it's my code not working or I have the wrong components or my circuit that is wrong, or if it's the general approach of my code that is wrong. It keeps indicating that the RPM is 0 whatever I do.

I'm rather new at Arduino and electronics, I'm just getting started so bare with me please.
As extra info I have mostly used ChatGPT for this code.

// Define the pins
const int irLedPin = 7;
const int phototransistorPin = A0;

// Variables to store the RPM calculation
volatile unsigned long lastMillis = 0;
volatile unsigned int revolutions = 0;
unsigned long rpm = 0;

void setup() {
  pinMode(irLedPin, OUTPUT);
  pinMode(phototransistorPin, INPUT);
  Serial.begin(9600);

  // Attach an interrupt to the phototransistor pin
  attachInterrupt(digitalPinToInterrupt(phototransistorPin), calculateRPM, RISING);
}

void loop() {
  // Turn on the IR LED
  digitalWrite(irLedPin, HIGH);

  // Print RPM every second
  if (millis() - lastMillis >= 1000) {
    noInterrupts();
    rpm = revolutions * 60; // Convert revolutions per second to RPM
    revolutions = 0;
    lastMillis = millis();
    interrupts();

    // Print the RPM value
    Serial.print("RPM: ");
    Serial.println(rpm);
  }
}

// ISR to count revolutions
void calculateRPM() {
  revolutions++;
}

Thank you to anyone who helps.

Is the RPM of the motor > 60 ? Also show how you've wired the photo transistor.

The RPM will be high, max 3500.

The phototransistor is the black one. The cathode being connected to 5v.

Use pin 2 or 3 for the photo transistor insteadof A0. Only these pins can be configured for external interrupts on a Uno. A transistor does not have a cathode.

Yeah, I meant collector instead of cathode. Using pin 2 or 3 would be instead of using A0 to read the transistor?

Exactly.

Unfortunately I'm still stuck with the same result, it stills shows me that RPM is 0 on the serial monitor, here's the new code using pin 2.

// Define the pins
const int irLedPin = 7;
const int phototransistorPin = 2; // Use pin 2 for the phototransistor

// Variables to store the RPM calculation
volatile unsigned long lastMillis = 0;
volatile unsigned int revolutions = 0;
unsigned long rpm = 0;

void setup() {
  pinMode(irLedPin, OUTPUT);
  pinMode(phototransistorPin, INPUT);
  Serial.begin(9600);

  // Attach an interrupt to the phototransistor pin
  attachInterrupt(digitalPinToInterrupt(phototransistorPin), calculateRPM, RISING);
}

void loop() {
  // Turn on the IR LED
  digitalWrite(irLedPin, HIGH);

  // Print RPM every second
  if (millis() - lastMillis >= 1000) {
    noInterrupts();
    rpm = revolutions * 60; // Convert revolutions per second to RPM
    revolutions = 0;
    lastMillis = millis();
    interrupts();

    // Print the RPM value
    Serial.print("RPM: ");
    Serial.println(rpm);
  }
}

// ISR to count revolutions
void calculateRPM() {
  revolutions++;
}

I'm beginning to think about simply using different components, I simply no undertandin of where I'm wrong and it's frustrating me

Your layout seems perfect for testing this but these can possibly go wrong:

IR led
Ensure it is correctly wired.
You could even replace it with a red led but that must be nearer the photo transistor but at least you would see it glowing.

Photo transistor
Wrong polarity - just try changing the two connections and see what happens.
If the room is very bright, the photo transistor could already be saturated or light leaking from the led could already blind it.
Try disconnecting the led and reconnecting it which watching the serial console.

If all else fails, then connect the photo transistor to an analog pin and simply read it in the loop(). Something like:

void setup() {
  Serial.begin(9600) ;
}

void loop() {
  Serial.println( analogRead( A0 ) ) ; // photo transistor on A0
  delay(500) ;
}

darken the room then shine a bright light on the photo transistor to see if there is any life in it by watching the serial console.

supream,
I've tested the code that you posted in post #7.

As I don't have your hardware, I have simulated the output of the phototransistor using a function generator.

I can confirm that the code works correctly, so the problem must be with your hardware setup.

Here is an oscilloscope trace showing a 100Hz, 1% duty cycle pulse fed into pin2.

  • Channel 1 - yellow trace - Input signal to pin 2.
  • Channel 3 - blue trace - Serial output from pin 1.


You can see that the decoded serial output reads 'RPM: 6000' which is correct for 100Hz input frequency.

Hi, @supream

Do you have a DMM? Digital MultiMeter?

Can you please do this test.

Connect the DMM, in DC Volts mode, across the phototransistor.

Measure the voltage with the path between the LED and the transistor, open and then interrupted by an object.

Tom.. :smiley: :+1: :coffee: :australia:

Hi,
That LED is not going to work like that, it is shorted out on the protoboard.
Measure the voltage across the LED....

Tom... :smiley: :+1: :coffee: :australia:

2 Likes

Well spotted Tom.

I had studied the phototransistor closely, but missed that one.

You could modify your code to turn an LED on and off to indicate the state of the phototransistor's output. You can then use this to set-up your hardware.

Something like:

// Define the pins
const int irLedPin = 7;
const int phototransistorPin = 2; // Use pin 2 for the phototransistor
const int monitorPin = 13;

// Variables to store the RPM calculation
volatile unsigned long lastMillis = 0;
volatile unsigned int revolutions = 0;
unsigned long rpm = 0;

void setup() {
  pinMode(irLedPin, OUTPUT);
  pinMode(phototransistorPin, INPUT);
  pinMode(monitorPin, OUTPUT);
  Serial.begin(9600);

  // Attach an interrupt to the phototransistor pin
  attachInterrupt(digitalPinToInterrupt(phototransistorPin), calculateRPM, RISING);

    // Turn on the IR LED
  digitalWrite(irLedPin, HIGH);
}

void loop() {

  // use monitorPin to indicate the state of the phototransistor
  digitalWrite(monitorPin,(digitalRead(phototransistorPin)));

  // Print RPM every second
  if (millis() - lastMillis >= 1000) {
    noInterrupts();
    rpm = revolutions * 60; // Convert revolutions per second to RPM
    revolutions = 0;
    lastMillis = millis();
    interrupts();

    // Print the RPM value
    Serial.print("RPM: ");
    Serial.println(rpm);
  }
}

// ISR to count revolutions
void calculateRPM() {
  revolutions++;
}

I've used pin13 to use the internal LED on a Uno R3.

Note that I have moved the line that turns on the LED from loop() to setup() - you only need to turn it on once.

  • Channel 1 - yellow trace - Input signal to pin 2.
  • Channel 2 - red trace - monitorPin shows the state of the phototransistor.
  • Channel 3 - blue trace - Serial output from pin 1.

You can use this to set-up your hardware. You can see the LED blink at low speed.

Hi to all for your help

I've decided to restart everything and go step by step, and I've gotten the transistor to work.
Turns out I also had the transistor connected the wrong way...
I just used this simple wiring and code, this time using 5v to power the IR LED and not the digital pins.

const int phototransistorPin = A0;

void setup() {
  Serial.begin(115200);
  pinMode(phototransistorPin, INPUT);
}

void loop() {
  int sensorValue = analogRead(phototransistorPin);
  Serial.println(sensorValue);
  delay(100); 
}

I get a 500 value when the LED shines into the transistor and 563 when it isn't. Now time to try and use this for rpm calculation.

and now using the same wiring, I now have a code that works for calculating RPM! :grinning:

const int phototransistorPin = A0; // Pin for the phototransistor
const int threshold = 530;         // Threshold value for detecting the reflective tape
volatile unsigned int revolutionCount = 0;
unsigned long lastMillis = 0;
unsigned long rpm = 0;

void setup() {
  Serial.begin(115200);
  pinMode(phototransistorPin, INPUT);
}

void loop() {
  // Read the value from the phototransistor
  int sensorValue = analogRead(phototransistorPin);

  // Detect threshold crossing
  static int lastSensorValue = sensorValue;
  if (lastSensorValue > threshold && sensorValue <= threshold) {
    revolutionCount++;
  }
  lastSensorValue = sensorValue;

  // Calculate and print RPM every second
  if (millis() - lastMillis >= 1000) {
    noInterrupts(); // Disable interrupts while calculating RPM
    rpm = revolutionCount * 60; // Convert revolutions per second to RPM
    revolutionCount = 0;
    lastMillis = millis();
    interrupts(); // Re-enable interrupts

    // Print RPM value
    Serial.print("RPM: ");
    Serial.println(rpm);
  }

  delay(10);
}

might need improvements, right now it's precision is low, only giving values in with a 60 interval, but this code will only be used for high rpms, from around 1500 to 3500. Thanks all for your help and if you have any suggestions feel free

Hi, @supream

A change from 500 to 563 is not much.

Or you might need to add an extra transistor to interface with the Arduino.

There are other ready made IR/phototransistor assemblies designed for Arduino.
Such as these, they use IR bounced of an object to detect it.

The circuit is similar to this;


It gives a digital HIGH/LOW output.

Tom... :smiley: :+1: :coffee: :australia: