Problem facing in RPM Sensing with mega2560 and LM393 Optical coupling sensor

Hi

i have tried to make engine monitoring dispaly. This Oled display will shows Engine rpm,engine working hours,Current date & time and Engine RPM.

I have facing the problem in engine rpm display/Serial print. When I run my program I read in my serial monitor Its showing the only last digit of rpm or erratic value.

I have used
1/Arduino mega 2560,
2/RTC Module
3/Temp sensor
4/LM393 Optical coupling speed sensor.

Only i have facing the problem in RPM reading.

Here is my code

#include <EEPROM.h>
#include <Wire.h>
#include <Adafruit_GFX.h>////////////////oled
#include <Adafruit_SSD1306.h>////////////oled

#include <OneWire.h>/////////////////////Temp sensor
#include <DallasTemperature.h>///////////Temp sensor
#define ONE_WIRE_BUS A0//////////////////Temp sensor
OneWire oneWire(ONE_WIRE_BUS);//////////Temp sensor
DallasTemperature sensors(&oneWire);////Temp sensor
#define ONE_WIRE_BUS A0////////////////Temp sensor

#define OLED_Address 0x3C///////////////oled
Adafruit_SSD1306 oled(1);///////////////oled
//#include "Wire.h"
#define WIDTH     128//////////////////oled
#define HEIGHT     64/////////////////oled
#define NUM_PAGE    8/////////////////oled

#include "RTClib.h"///////////////////// rtc
RTC_DS3231 rtc;////////////////////////// rtc
#define deviceAddress 0b1101000   //0x68

#define NUMFLAKES 10/////////////////oled
#define XPOS 0///////////////////////oled
#define YPOS 1///////////////////////oled
#define DELTAY 2///////////////////oled
#define LOGO16_GLCD_HEIGHT 16///////oled
#define LOGO16_GLCD_WIDTH  16//////oled

int interval = 1000;
int counter = 0 ;
int counter1 = 0 ;
uint32_t frameStartAtMs ;
int Pre;
int Pre1;


const uint8_t Key = 3;
const byte ledPin = 13;
const byte interruptPin = 2;
volatile byte state = LOW;
const uint8_t EngineCommand = 8;
float temp;
unsigned long time_now = 0;

int EventState = 0;
int addr_secnd = 0;
int addr_Minute = 1;
int addr_hrs1 = 2;
int addr_hrs2 = 3;
int addr_hrs3 = 4;
int addr_hrs4 = 5;
int addr_hrs5 = 6;

int secnd = 0;
int Minute = 0;
int hrs1 = 0;
int hrs2 = 0;
int hrs3 = 0;
int hrs4 = 0;
int hrs5 = 0;
int value_secnd;
int value_Minute;
int value_hrs1;
int value_hrs2;
int value_hrs3;
int value_hrs4;
int value_hrs5;
int x6;
int x7;
int x8;
const byte MOTOR1 = 18;  // Motor 1 Interrupt Pin - INT 0
const byte MOTOR2 = 19;
int period = 300;
unsigned long previousMillis = 0;
unsigned long currentMillis;

long lastTime = 1;
long seconds = EEPROM.read(addr_secnd);
long minutes = EEPROM.read(addr_Minute);
long hours = EEPROM.read(addr_hrs2);

void setSQW(uint8_t value)
{
  Wire.beginTransmission(deviceAddress); //device address and STSRT command are queued
  Wire.write(0x0E); //Control Register Address is queued
  Wire.write(0x00); //Data for Control Register is queued
  Wire.endTransmission(); //queued information are transferred under ACK; STOP
}

void setup()
{
  oled.begin(SSD1306_SWITCHCAPVCC, OLED_Address);
  rtc.begin();
  sensors.begin();
  Serial.begin(9600);
  Wire.begin();
  DateTime now = rtc.now();
  if (! rtc.begin())
  {
    Serial.println("Couldn't find RTC");
    while (1);
  }
  if (rtc.lostPower())
  {
    Serial.println("RTC lost power, lets set the time!");
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }
  pinMode(MOTOR1, INPUT);
  pinMode(MOTOR2, INPUT);
  Pre = digitalRead(18);
  Pre1 = digitalRead(19);
  frameStartAtMs = millis() ;

  pinMode(Key, INPUT);
  pinMode(EngineCommand, INPUT);
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), HourMeter, FALLING);
  setSQW(0x00);
}

String time()
{
  String dataString = "";
  DateTime now = rtc.now();
  dataString += String(now.year(), DEC);
  dataString += String('.');
  dataString += String(now.month(), DEC);
  dataString += String('.');
  dataString += String(now.day(), DEC);

  dataString += String("    ");
  dataString += String(now.hour(), DEC);
  dataString += String(':');
  dataString += String(now.minute(), DEC);
  dataString += String(':');
  dataString += String(now.second(), DEC);
  return dataString;
}

void loop() {

  if ( millis() - frameStartAtMs > interval ) {////ENGINE RPM
    Serial.println(counter * 60 / 20);
    Serial.println(counter1 * 60 / 20);
    frameStartAtMs = millis() ;
    counter = 0;
    counter1 = 0;
  }
  if (digitalRead(MOTOR1) != Pre)  ///ENGINE1
  {
    counter++;
    Pre = digitalRead(MOTOR1);

  }

  if (digitalRead(MOTOR2) != Pre1)////ENGINE2
  {
    counter1++;
    Pre1 = digitalRead(MOTOR2);

  }

  if ( digitalRead(Key) == HIGH)//KEY OFF-  DISPALY off
  {
    // oled1.clearDisplay();
    //oled1.display();
    oled.clearDisplay();
    oled.display();
  }
  else ////////////////////////////KEY ON-DISPLAY ON
  {
    oled.setTextSize(1);
    oled.setTextColor(BLACK, WHITE);
    oled.setCursor(60, 24);
    oled.print(" KEY ON ");
    oled.display();
    display();
  }

 // digitalWrite(ledPin, state);
  if (digitalRead(EngineCommand) == HIGH)/////at the time of engine shutoff ,Current reading registerd into eeprom
  {
    if (EventState < 1)
    {
      EEPROM.update(addr_secnd, seconds);
      EEPROM.update(addr_Minute, minutes);
      EEPROM.update(addr_hrs2, hours);
      Serial.print(EEPROM.read(addr_hrs2));
      Serial.print(EEPROM.read(addr_Minute));
      Serial.println(EEPROM.read(addr_secnd));
      EventState++;
    }
  }

}


void HourMeter() {
  if (digitalRead(EngineCommand) == LOW)////Engine ON-- Hour counting started.
  {

    EventState = 0;

    if (lastTime > 0)
    {
      seconds++;

    }
    if (seconds > 59)
    {

      minutes++;
      seconds = 0;

    }
    if (minutes > 59)
    {

      hours++;
      minutes = 0;
    }

    state = !state;
  }

}


void display()////oled will display the coolant temperature ,date&time and hour counting.
{
  sensors.requestTemperatures();
  temp = sensors.getTempCByIndex(0);
  oled.clearDisplay();
  oled.setTextSize(1);
  oled.setTextColor(WHITE, BLACK);
  oled.setCursor(0, 0);
  oled.print(time());
  oled.drawLine(0, 10, 127, 10, WHITE);
  oled.drawLine(0, 11, 127, 11, WHITE);

  oled.setCursor(0, 14);
  oled.print((hours));//,":",(minutes));
  oled.print(":");
  oled.print((minutes));//,":",(minutes));
  oled.print(":");
  oled.print((seconds));//,":",(minutes));
  oled.setCursor(0, 24);
  oled.print((temp));
  oled.display();

}

Serial monitor data

0
3
0
0
0
3
3
3
3
3
3
3
6
6

I am not well expert in programming so could somebody please tell me where am I wrong?

please help me on this matter.

Hi sreekanthmp,

interrupts can be used to measure rpm.
you have configured your interrupt

attachInterrupt(digitalPinToInterrupt(interruptPin), HourMeter, FALLING);

this means each time on IO-pin number 2 the voltage does a transistion from HIGH to LOW (This is called a falling edge)
your function hourmeter is called once. Only one time for each falling-edge.

The function named inside the attachInterrupt-command is called the InterruptServiceRoutine in short ISR

I guess your function hourmeter should count the hours of operation of the engine. This must be outside the function that gets executed when a falling-edge is detected on the interrupt-IO-pin.
You need another function that does not more than count up another counter. That's all the ISR is doing.
Attention! This variable has to be declared as volatile

volatile int RPM_pulses; //RPM_pulses is shared between ISR and main-code therefore defined as volative to prevent the variable from beeing optimized away by the compiler
void isr_counting_pulses() 
{
  RPM_pulses++;  // count up by 1
}

the interrupt setup looks this way

attachInterrupt(digitalPinToInterrupt(interruptPin), isr_counting_pulses, FALLING);

Then inside the loop()-function you caclulate the rpm based on the numbers of rpm_pulses.
Therefore you setup a timing-functionality that reads the variable RPM_pulses; after a certain amount of time

boolean TimePeriodIsOver (unsigned long &expireTime, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - expireTime >= TimePeriod )
  {
    expireTime = currentMillis; // set new expireTime
    return true;                // more time than TimePeriod) has elapsed since last time if-condition was true
  } 
  else return false;            // not expired
}
unsigned long RPM_Timer = 0;

inside loop()

  if (  TimePeriodIsOver(RPM_Timer,1000)  ) // 1000 means 1000 milliseconds = 1 second
    {
      Serial.print("RPM:");
      Serial.println(RPM_pulses * 60);
      RPM_pulses = 0;
    }

So every 1 second the counted pulses (= rotations per second) * 60 = rotations per minute
This is not a complete tested and working example just the main parts of the code.

My redommendation is write a test-porgram that does nothing more than counting rpm and show it on the serial monitor and learn on this test-code how to use it.
Then add this well tested code to your other code

You might think this will need more time. No it will need less time because if this part of the code is well tested you don't have to analyse ALL your code for some fault, because you now this part of the code is working the error has to be somewhere else

best regards Stefan

It may be that all that lcd display code is slowing you down and you're missing pulses. At 3000 RPM with apparently 20 pulses per revolution, you're looking to capture 1000 pulses a second. I think, as suggested that interrupts would server you better.

But at least make a copy of your sketch and reduce it to something that does nothing but print the RPM as suggested by StefanL38. If the RPM is higher than what you're getting now it will be a valuable clue.

StefanL38 sir,Thank you for your reply.

I made one program for printing only the rpm in Serial monitor and its working (Not accurate reading but its working)

Code is Here:-

volatile int RPM_pulses; //RPM_pulses is shared between ISR and main-code therefore defined as volative to prevent the variable from beeing optimized away by the compiler
unsigned long RPM_Timer = 0;
const byte interruptPin = 18;
void isr_counting_pulses()
{
  RPM_pulses++;  // count up by 1
}
boolean TimePeriodIsOver (unsigned long &expireTime, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - expireTime >= TimePeriod )
  {
    expireTime = currentMillis; // set new expireTime
    return true;                // more time than TimePeriod) has elapsed since last time if-condition was true
  }
  else return false;            // not expired
}
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), isr_counting_pulses, FALLING);
}

void loop() {
  // put your main code here, to run repeatedly:


  if (  TimePeriodIsOver(RPM_Timer, 1000)  ) // 1000 means 1000 milliseconds = 1 second
  {
    Serial.print("RPM:");
    Serial.println(RPM_pulses * 60 / 20);
    RPM_pulses = 0;
  }
}

Serial monitor

RPM:309
RPM:267
RPM:267
RPM:264
RPM:267
RPM:279
RPM:282
RPM:255
RPM:258
RPM:282
RPM:249
RPM:255
RPM:255

boolean TimePeriodIsOver (unsigned long &expireTime, unsigned long TimePeriod)

1)Please clarify this line.I want to learn more about this (" &expireTime").Why this "&" symbol.?

2)How the following two functions relate to each other ?

boolean TimePeriodIsOver (unsigned long &expireTime, unsigned long TimePeriod) {
unsigned long currentMillis = millis();
if ( currentMillis - expireTime >= TimePeriod )
{
expireTime = currentMillis; // set new expireTime
return true; // more time than TimePeriod) has elapsed since last time if-condition was true
}
else return false; // not expired
}

AND

if ( TimePeriodIsOver(RPM_Timer,1000) ) // 1000 means 1000 milliseconds = 1 second
{
Serial.print("RPM:");
Serial.println(RPM_pulses * 60/20);
RPM_pulses = 0;
}

expecting favorable reply

How many RPM was the engine actually doing during your test?

wildbill:
How many RPM was the engine actually doing during your test?

0-400 rpm

Can you run some more tests with a number of different fixed RPMs and show us what the serial output looks like for each?

Sir,
I don't want more accuracy.

When i run StefanL38's program only for rpm serial printing its working.But when i add this in to the main program (posted in first ) serial monitor showing only one digit.

Post your new code.

You pay attention to each detail. Very good!

the & symbol means take adress of variable of the function-call instead of the value itself

*if ( TimePeriodIsOver(RPM_Timer,1000) ) *

the function TimePeriodIsOver has to store a new value inside variable RPM_Timer. And to be able to do this the function needs the memoryadress of the variable RPM_Timer. The &-symbol does this.

Through this variant you can use the function for any variable of type unsigned long without thinking more about it.
This is the definition of function TimePeriodIsOver

boolean TimePeriodIsOver (unsigned long &expireTime, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - expireTime >= TimePeriod )
  {
    expireTime = currentMillis; // set new expireTime
    return true;                // more time than TimePeriod) has elapsed since last time if-condition was true
  }
  else return false;            // not expired
}

and this is the "use" of the function

  if (  TimePeriodIsOver(RPM_Timer, 1000)  ) // 1000 means 1000 milliseconds = 1 second

why did you code

Serial.println(RPM_pulses * 60 / 20);

rpm_pulses multiplid with 60 gives the impulses per minute. Why would you divide it again by 20?
Does one revolution have 20 pulses?

best regards Stefan

wildbill:
Post your new code.

My code is here

#include <EEPROM.h>
#include <Wire.h>
#include <Adafruit_GFX.h>////////////////oled
#include <Adafruit_SSD1306.h>////////////oled

#include <OneWire.h>/////////////////////Temp sensor
#include <DallasTemperature.h>///////////Temp sensor
#define ONE_WIRE_BUS A0//////////////////Temp sensor
OneWire oneWire(ONE_WIRE_BUS);//////////Temp sensor
DallasTemperature sensors(&oneWire);////Temp sensor
#define ONE_WIRE_BUS A0////////////////Temp sensor

#define OLED_Address 0x3C///////////////oled
Adafruit_SSD1306 oled(1);///////////////oled
//#include "Wire.h"
#define WIDTH     128//////////////////oled
#define HEIGHT     64/////////////////oled
#define NUM_PAGE    8/////////////////oled

#include "RTClib.h"///////////////////// rtc
RTC_DS3231 rtc;////////////////////////// rtc
#define deviceAddress 0b1101000   //0x68

#define NUMFLAKES 10/////////////////oled
#define XPOS 0///////////////////////oled
#define YPOS 1///////////////////////oled
#define DELTAY 2///////////////////oled
#define LOGO16_GLCD_HEIGHT 16///////oled
#define LOGO16_GLCD_WIDTH  16//////oled



volatile int RPM_pulses; //RPM_pulses is shared between ISR and main-code therefore defined as volative to prevent the variable from beeing optimized away by the compiler
unsigned long RPM_Timer = 0;
const byte interruptPin1 = 18;

const uint8_t Key = 3;
const byte ledPin = 13;
const byte interruptPin = 2;
volatile byte state = LOW;
const uint8_t EngineCommand = 8;
float temp;
unsigned long time_now = 0;

int EventState = 0;
int addr_secnd = 0;
int addr_Minute = 1;
int addr_hrs1 = 2;
int addr_hrs2 = 3;
int addr_hrs3 = 4;
int addr_hrs4 = 5;
int addr_hrs5 = 6;

int secnd = 0;
int Minute = 0;
int hrs1 = 0;
int hrs2 = 0;
int hrs3 = 0;
int hrs4 = 0;
int hrs5 = 0;
int value_secnd;
int value_Minute;
int value_hrs1;
int value_hrs2;
int value_hrs3;
int value_hrs4;
int value_hrs5;
int x6;
int x7;
int x8;

long lastTime = 1;
long seconds = EEPROM.read(addr_secnd);
long minutes = EEPROM.read(addr_Minute);
long hours = EEPROM.read(addr_hrs2);

void setSQW(uint8_t value)
{
  Wire.beginTransmission(deviceAddress); //device address and STSRT command are queued
  Wire.write(0x0E); //Control Register Address is queued
  Wire.write(0x00); //Data for Control Register is queued
  Wire.endTransmission(); //queued information are transferred under ACK; STOP
}
void isr_counting_pulses()
{
  RPM_pulses++;  // count up by 1
}
boolean TimePeriodIsOver (unsigned long &expireTime, unsigned long TimePeriod) 
{
  unsigned long currentMillis  = millis();
  if ( currentMillis - expireTime >= TimePeriod )
  {
    expireTime = currentMillis; // set new expireTime
    return true;                // more time than TimePeriod) has elapsed since last time if-condition was true
  }
  else return false;            // not expired
}
void setup()
{
  oled.begin(SSD1306_SWITCHCAPVCC, OLED_Address);
  rtc.begin();
  sensors.begin();
  Serial.begin(9600);
  Wire.begin();
  DateTime now = rtc.now();
  if (! rtc.begin())
  {
    Serial.println("Couldn't find RTC");
    while (1);
  }
  if (rtc.lostPower())
  {
    Serial.println("RTC lost power, lets set the time!");
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }
 
  pinMode(Key, INPUT);
  pinMode(EngineCommand, INPUT);
  pinMode(interruptPin, INPUT_PULLUP);
  pinMode(interruptPin1, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), HourMeter, FALLING);
  attachInterrupt(digitalPinToInterrupt(interruptPin1), isr_counting_pulses, FALLING);
  setSQW(0x00);
oled.clearDisplay();
    oled.display();
}

String time()
{
  String dataString = "";
  DateTime now = rtc.now();
  dataString += String(now.year(), DEC);
  dataString += String('.');
  dataString += String(now.month(), DEC);
  dataString += String('.');
  dataString += String(now.day(), DEC);

  dataString += String("    ");
  dataString += String(now.hour(), DEC);
  dataString += String(':');
  dataString += String(now.minute(), DEC);
  dataString += String(':');
  dataString += String(now.second(), DEC);
  return dataString;
}


void loop() {

 
if (  TimePeriodIsOver(RPM_Timer, 1000)  ) // 1000 means 1000 milliseconds = 1 second
  {
    Serial.print("RPM:");
    Serial.println(RPM_pulses * 60 / 20);/// 20 SLOTS PER REVOLUTION
    oled.setTextSize(1);
    oled.setTextColor(WHITE,BLACK);
    oled.setCursor(60, 14);
    oled.print(RPM_pulses * 60 / 20);
   
    oled.display();
     
     //oled.display();
    RPM_pulses = 0;
  }

 if ( digitalRead(Key) == HIGH)//KEY OFF NO DISPALY
  {
    // oled1.clearDisplay();
    //oled1.display();
    oled.clearDisplay();
    oled.display();
  }
  else ////////////////////////////KEY ON DISPLAY ON
  {
    oled.setTextSize(1);
    oled.setTextColor(BLACK, WHITE);
    oled.setCursor(60, 24);
    oled.print(" KEY ON ");
    oled.display();
}
    display();
  

 // digitalWrite(ledPin, state);
  if (digitalRead(EngineCommand) == HIGH)/////at the time of engine shutoff ,Current reading registerd into eeprom
  {
    if (EventState < 1)
    {
      EEPROM.update(addr_secnd, seconds);
      EEPROM.update(addr_Minute, minutes);
      EEPROM.update(addr_hrs2, hours);
      Serial.print(EEPROM.read(addr_hrs2));
      Serial.print(EEPROM.read(addr_Minute));
      Serial.println(EEPROM.read(addr_secnd));
      EventState++;
    }
  }

}

void HourMeter() {
  if (digitalRead(EngineCommand) == LOW)////Engine ON-- Hour counting started.
  {

    EventState = 0;

    if (lastTime > 0)
    {
      seconds++;

    }
    if (seconds > 59)
    {

      minutes++;
      seconds = 0;

    }
    if (minutes > 59)
    {

      hours++;
      minutes = 0;
    }

    state = !state;
  }

}



void display()////oled will display the coolant temperature ,date&time and hour counting.
{
  sensors.requestTemperatures();
  temp = sensors.getTempCByIndex(0);
  //oled.clearDisplay();
  oled.setTextSize(1);
  oled.setTextColor(WHITE, BLACK);
  oled.setCursor(0, 0);
  oled.print(time());
  oled.drawLine(0, 10, 127, 10, WHITE);
  oled.drawLine(0, 11, 127, 11, WHITE);

  oled.setCursor(0, 14);
  oled.print((hours));//,":",(minutes));
  oled.print(":");
  oled.print((minutes));//,":",(minutes));
  oled.print(":");
  oled.print((seconds));//,":",(minutes));
  oled.setCursor(0, 24);
  oled.print((temp));
  
  oled.display();
 
}

Now RPM serial printing is working.

RPM:0//engine off
RPM:0
RPM:0
RPM:0
RPM:213//engine ON
RPM:252
RPM:228
RPM:255
RPM:261
RPM:297
RPM:255
RPM:264
RPM:258

But oled shows rpm even engine in off condition.

Often, such displays leave characters behind if you don't clear them. I'll guess that what you're seeing happened at engine off after it had been running. It's likely printing zero but there are two digits after it left over from when there really was some RPM.

StefanL38:
You pay attention to each detail. Very good!

Thank you.

the & symbol means take adress of variable of the function-call instead of the value itself

*if ( TimePeriodIsOver(RPM_Timer,1000) ) *

It means we can execute the function with variable address? In this function "RPM_Timer" is variable and its in initially zero value and 1000 Time period, its a fix value.Is it my concept is correct?

and this is the "use" of the function

  if (  TimePeriodIsOver(RPM_Timer, 1000)  ) // 1000 means 1000 milliseconds = 1 second

Why this "if()" use this place..what is the meaning ,can you clarify this part?

rpm_pulses multiplid with 60 gives the impulses per minute. Why would you divide it again by 20?
Does one revolution have 20 pulses?

Yes.... 20 slots/rev

wildbill:
Often, such displays leave characters behind if you don't clear them.

if (  TimePeriodIsOver(RPM_Timer, 1000)  ) // 1000 means 1000 milliseconds = 1 second
  {
    Serial.print("RPM:");
    Serial.println(RPM_pulses * 60 / 20);/// 20 SLOTS PER REVOLUTION
    oled.setTextSize(1);
    oled.setTextColor(WHITE,BLACK);
    oled.setCursor(60, 14);
    oled.print(RPM_pulses * 60 / 20);
    oled.clearDisplay();////////////////////////added clear display
    oled.display();
     
     //oled.display();
    RPM_pulses = 0;
  }

If i add " oled.clearDisplay()" oled display shows RPM in blinking mode so that i removed that function.

It's likely printing zero but there are two digits after it left over from when there really was some RPM.

Exactly!!How i can over come this problem?

any if-condition contains a expression of type boolean

example

digitalRead(Key) == HIGH) the "Key) == HIGH" evaluate to true or false

the function TimePeriodIsOver gives back a boolean value right away
if you would code

Serial.println(TimePeriodIsOver(RPM_Timer,1000));

the serial monitor would show

true

or

false

inside the if-condition of your loop()
you have added some additonal commands
this means two things:

1.) it might be that all these additional commands need quite a big amount of milliseconds to get exectuted
2.) the intervall when the if-condition becomes true might be more than exactly 1000 milliseconds

to improve precision of the rpm-calculation try this version

static is a variation of defining a variable that makes the compiler keep the variable instead of destroying and re-defining it each time a function is called

the rest of the explanation is written as comments inside the code

void loop() {
  static unsigned long ActualMillis; 
  static unsigned long LastPeriodMillis; 

  int the_real_RPM;
 
if (  TimePeriodIsOver(RPM_Timer, 1000)  ) // 1000 means 1000 milliseconds = 1 second
  {
    pulsesOfPeriod = RPM_pulses; // make copy of counted pulses
    RPM_pulses = 0;              // immediately set RPM_pulses to zero to not miss any pulses
    ActualMillis = millis();     // make snapshot of millis() in case a little bit more than period has elapsed

    TimeDifferenceSinceLastPeriod = ActualMillis - LastPeriodMillis; calculate the real timedifference
    LastPeriodMillis = ActualMillis;  // as ActuallMillis is no longer used update LastPeriodMillis 

    // millis() delivers milliseconds so TimeDifferenceSinceLastPeriod contains the number off milliseconds between
    // the last entering of this if-condition and the actual entering
    // as we measure around 1 second this is 1000 milliseconds  * 60 / 20 evaluates to 3
    // multiplying pulsesOfPeriod with 3 * 1000 gives the real_rpm * 1000 dividing with TimeDifferenceSinceLastPeriod 
    // calculates the real RPM more precisely
    // example with numbers
    // lets say rpm is 300 1/min = so in one second 5 rpm = 5 * 20 = 100 pulses

    // lets say LastPeriodMillis has value 2500 when entering the if-condition not only exact 1000 milliseconds has passed by 
    // but 1100 milliseconds
    // in 1100 milliseconds 110 pulses were counted

    // actualMillis will contain 3600
    // TimeDifferenceSinceLastPeriod = 3600 - 2500 = 1100 milliseconds
    // the_real_RPM = 110 * 3000 / 1100 = 300
    the_real_RPM = pulsesOfPeriod * 3000 / TimeDifferenceSinceLastPeriod;
    
    Serial.print("RPM:");
    Serial.println(the_real_RPM );
    oled.setTextSize(1);
    oled.setTextColor(WHITE,BLACK);
    oled.setCursor(60, 14);
    oled.print(the_real_RPM );
   
    oled.display();
     
     //oled.display();
    
  }

best regards Stefan

sreekanthmp:
If i add " oled.clearDisplay()" oled display shows RPM in blinking mode so that i removed that function.

Exactly!!How i can over come this problem?

Before you print the RPM, set the same cursor position and print some spaces, then reset the cursor and print RPM.

Before you print the RPM, set the same cursor position and print some spaces, then reset the cursor and print RPM.

Sorry!! I don't understand please explain once more.

Supposing you printed, "335". Now you want to print "2". You print "2" in the first location, now it reads, "235".

See?

You need to print " " before printing "2" so the the stray "35" is erased.

StefanL38:
any if-condition contains a expression of type boolean

example

the rest of the explanation is written as comments inside the code

void loop() {

static unsigned long ActualMillis;
 static unsigned long LastPeriodMillis;

int the_real_RPM;

if (  TimePeriodIsOver(RPM_Timer, 1000)  ) // 1000 means 1000 milliseconds = 1 second
 {
   pulsesOfPeriod = RPM_pulses; // make copy of counted pulses
   RPM_pulses = 0;              // immediately set RPM_pulses to zero to not miss any pulses
   ActualMillis = millis();     // make snapshot of millis() in case a little bit more than period has elapsed

TimeDifferenceSinceLastPeriod = ActualMillis - LastPeriodMillis; calculate the real timedifference
   LastPeriodMillis = ActualMillis;  // as ActuallMillis is no longer used update LastPeriodMillis

// millis() delivers milliseconds so TimeDifferenceSinceLastPeriod contains the number off milliseconds between
   // the last entering of this if-condition and the actual entering
   // as we measure around 1 second this is 1000 milliseconds  * 60 / 20 evaluates to 3
   // multiplying pulsesOfPeriod with 3 * 1000 gives the real_rpm * 1000 dividing with TimeDifferenceSinceLastPeriod
   // calculates the real RPM more precisely
   // example with numbers
   // lets say rpm is 300 1/min = so in one second 5 rpm = 5 * 20 = 100 pulses

// lets say LastPeriodMillis has value 2500 when entering the if-condition not only exact 1000 milliseconds has passed by
   // but 1100 milliseconds
   // in 1100 milliseconds 110 pulses were counted

// actualMillis will contain 3600
   // TimeDifferenceSinceLastPeriod = 3600 - 2500 = 1100 milliseconds
   // the_real_RPM = 110 * 3000 / 1100 = 300
   the_real_RPM = pulsesOfPeriod * 3000 / TimeDifferenceSinceLastPeriod;
   
   Serial.print("RPM:");
   Serial.println(the_real_RPM );
   oled.setTextSize(1);
   oled.setTextColor(WHITE,BLACK);
   oled.setCursor(60, 14);
   oled.print(the_real_RPM );
 
   oled.display();
   
    //oled.display();
   
 }



best regards Stefan

i have tried ,here is my code

volatile int RPM_pulses; //RPM_pulses is shared between ISR and main-code therefore defined as volative to prevent the variable from beeing optimized away by the compiler
unsigned long RPM_Timer = 0;
const byte interruptPin = 18;

int pulsesOfPeriod;
int the_real_RPM;

void isr_counting_pulses()
{
  RPM_pulses++;  // count up by 1
}
boolean TimePeriodIsOver (unsigned long &expireTime, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - expireTime >= TimePeriod )
  {
    expireTime = currentMillis; // set new expireTime
    return true;                // more time than TimePeriod) has elapsed since last time if-condition was true
  }
  else return false;            // not expired
}
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), isr_counting_pulses, FALLING);
}

void loop() {
  // put your main code here, to run repeatedly:
static unsigned long ActualMillis;
  static unsigned long LastPeriodMillis;
static unsigned long TimeDifferenceSinceLastPeriod;
  if (  TimePeriodIsOver(RPM_Timer, 1000)  ) // 1000 means 1000 milliseconds = 1 second
  {
    pulsesOfPeriod = RPM_pulses; // make copy of counted pulses
    RPM_pulses = 0;              // immediately set RPM_pulses to zero to not miss any pulses
    ActualMillis = millis(); 
     Serial.println(pulsesOfPeriod);
     Serial.println(ActualMillis);
     Serial.println( LastPeriodMillis);
    TimeDifferenceSinceLastPeriod = ActualMillis - LastPeriodMillis; //calculate the real timedifference
    LastPeriodMillis = ActualMillis; 
    Serial.println( TimeDifferenceSinceLastPeriod);
    the_real_RPM = pulsesOfPeriod * 6000 / TimeDifferenceSinceLastPeriod;
    Serial.print("RPM:");
    Serial.println(the_real_RPM);
    //RPM_pulses = 0;
  }
}

But serial out is erratic .

RPM:-30429
89           /////pulsesOfPeriod
123000    //ActualMillis
122000   ///LastPeriodMillis
1000      ///// TimeDifferenceSinceLastPeriod
RPM:9
95
124000
123000
1000
RPM:-30429/////ooof!!!!
91