Tachometer using arduino

Build Tachometer or RPM counter using Optocoupler with Arduino

We will learn how to use an optocoupler sensor module as an RPM counter or Tachometer.

Before we start, we should understand how this sensor works.

This sensor uses an infrared LED on one side, and there is a photosensor on another side. So when there’s no obstacle between these sides the infrared light reaches the photosensor and gives the signal to the circuit. And this module will output 5V or HIGH state.

But the opposite, if there’s an obstacle between these sides, the photosensor will not detect light and no signal given to the circuit. So the output of this module will be LOW.

So the conclusion is, if there’s an obstacle it will output HIGH

And if there isn’t it will output LOW.

Arduino Tachometer Wiring diagram

In this example, I print the value from the sensor to an I2C LCD. If you need a detailed tutorial on how to use this LCD you can find it here.

Optocoupler as Tachometer wiring diagram

Arduino Tachometer Code

With the conclusion I mentioned earlier, we can easily read the signal with an Arduino. We will try to read every time the signal from the sensor is falling or changing from HIGH to LOW. Or from there’s obstacle to no obstacle. To do this we will need an interrupt, so I will use interrupt at pin 2 and every interrupt happened. I will call function count, and interrupt will happen every FALLING. And I make the LCD print the holes every 500ms. The count function that called every interrupt just increments the value of holes. This sensor should always use interrupt for accuracy. So it should always be on the interrupt pin, for Arduino UNO or nano. The interrupt pins are only available on pin 2 and 3.

First Experiment, reading signal and count it as hole with optocoupler

/* Code made by miliohm.com
     Visit our Youtube channel miliohm for more tutorials */

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2); // set the LCD address to 0x27 for a 16 chars and 2 line display

float rpm = 0;

int pid;
unsigned long millisBefore;
volatile int holes;

void setup()
{
  Serial.begin(9600);
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("Speed Sensor");
  lcd.setCursor(0, 1);
  lcd.print("Test");
  pinMode(2, INPUT);
  attachInterrupt(digitalPinToInterrupt(2), count, FALLING);
  delay(1000);
  lcd.clear();
}

void loop()
{
  if (millis() - millisBefore > 500) {
    print_to_LCD();
    millisBefore = millis();
  }
  delay(200);
}

void print_to_LCD() {
  lcd.setCursor(0, 0);
  lcd.print("Holes : ");
  lcd.print(holes);
}

void count() {
  holes++;
}

OK, the next question is, how to determine if the disc has already turned one time?

Every one turn the sensor will detect 12 holes, right? To count how much turn or rotation is just divide the number of holes by 12.

Second Experiment, Measuring the rotation / turn with optocoupler

/* Code made by miliohm.com
     Visit our Youtube channel miliohm for more tutorials */

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2); // set the LCD address to 0x27 for a 16 chars and 2 line display

float rpm,turn = 0;

int pid;
unsigned long millisBefore;
volatile int holes;

void setup()
{
  Serial.begin(9600);
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("Speed Sensor");
  lcd.setCursor(0, 1);
  lcd.print("Test");
  pinMode(2, INPUT);
  attachInterrupt(digitalPinToInterrupt(2), count, FALLING);
  delay(1000);
  lcd.clear();
}

void loop()
{
  if (millis() - millisBefore > 500) {
    turn = (holes/12.0);
    print_to_LCD();
    millisBefore = millis();
  }
}

void print_to_LCD() {
  lcd.setCursor(0, 0);
  lcd.print("Holes : ");
  lcd.print(holes);
  lcd.setCursor(0, 1);
  lcd.print("Turn : ");
  lcd.print(turn);
}

void count() {
  holes++;
}

Measuring RPM for one minute

/* Code made by miliohm.com
     Visit our Youtube channel miliohm for more tutorials */

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2); // set the LCD address to 0x27 for a 16 chars and 2 line display

float rpm = 0;

int pid;
unsigned long millisBefore;
volatile int holes;

void setup()
{
  Serial.begin(9600);
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("Speed Sensor");
  lcd.setCursor(0, 1);
  lcd.print("Test");
  pinMode(2, INPUT);
  attachInterrupt(digitalPinToInterrupt(2), count, FALLING);
  delay(1000);
  lcd.clear();
}

void loop()
{
  print_to_LCD();
  if (millis() - millisBefore > 60000) {
    rpm = holes / 12.0;
    holes = 0;
    millisBefore = millis();
  }
  delay(200);
}

void print_to_LCD() {
  lcd.setCursor(0, 0);
  lcd.print("Holes : ");
  lcd.print(holes);
  lcd.print("    ");
  lcd.setCursor(0, 1);
  lcd.print("RPM   : ");
  lcd.print(rpm);
  lcd.print("    ");
}

void count() {
  holes++;
}

So, to count faster we can use the sampling method. For example, if I want to count only for 6 seconds, it needs to multiply by 10. Because 1 minute has 60 seconds. And if the speed you want to measure is HIGH enough, you can count only for 1 second.

This 1 second time you can consider by yourself how fast is the system you wanted to measure. A faster system will be enough with shorter sampling. The slower system will need more time to sample to get accurate data.

OK, now I wanted to try to measure the rpm real for one minute. So I have to set the millis delay every 60000 milliseconds and no need to multiply this value. And reset the holes by setting the value to zero. So it will be recounting after one minute or 60000 milliseconds. So Never forget to reset the number of holes. so this cycle will occur every one minute. Every cycle this function will calculate the rpm and reset the number of holes over and over again.

The Last Experiment, measuring RPM with sampling method

/* Code made by miliohm.com
     Visit our Youtube channel miliohm for more tutorials */

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2); // set the LCD address to 0x27 for a 16 chars and 2 line display

float rpm = 0;

int pid;
unsigned long millisBefore;
volatile int holes;

void setup()
{
  Serial.begin(9600);
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("Speed Sensor");
  lcd.setCursor(0, 1);
  lcd.print("Test");
  pinMode(2, INPUT);
  attachInterrupt(digitalPinToInterrupt(2), count, FALLING);
  delay(1000);
  lcd.clear();
}

void loop()
{
  print_to_LCD();
  if (millis() - millisBefore > 1000) {
    rpm = (holes / 12.0)*60;
    holes = 0;
    millisBefore = millis();
  }
  delay(100);
}

void print_to_LCD() {
  lcd.setCursor(0, 0);
  lcd.print("Holes : ");
  lcd.print(holes);
  lcd.print("    ");
  lcd.setCursor(0, 1);
  lcd.print("RPM   : ");
  lcd.print(rpm);
  lcd.print("    ");
}

void count() {
  holes++;
}

Here’s the complete video tutorial :

Leave a Reply

Your email address will not be published. Required fields are marked *