#include <Servo.h>
Servo rpmServo;
// ================= PIN =================
const byte IR_PIN = 2;
const byte SERVO_PIN = 9;
// ================= RPM SETTINGS =================
const float MAX_RPM = 10000.0;
// Shaft par 1 mark = 1 pulse
const byte PULSES_PER_REV = 1;
// ================= SENSOR FILTER =================
// 10,000 RPM par 1 pulse ≈ 6000 microseconds
// Is se kam interval ko noise samjha jayega
const unsigned long MIN_PULSE_INTERVAL = 2000;
// Agar itne time tak pulse na aaye to RPM zero hona shuru
const unsigned long NO_PULSE_TIMEOUT = 2000000UL; // 2 seconds
// ================= INTERRUPT VARIABLES =================
volatile unsigned long lastPulseMicros = 0;
volatile unsigned long pulseInterval = 0;
volatile bool newPulse = false;
// ================= RPM VARIABLES =================
float measuredRPM = 0;
float filteredRPM = 0;
// ================= SERVO VARIABLES =================
float currentAngle = 0;
float targetAngle = 0;
// Servo needle movement
// 0.5 = very smooth
const float SERVO_STEP = 0.5;
// Servo update speed
const unsigned long SERVO_UPDATE = 5;
unsigned long lastServoUpdate = 0;
// ================= RPM FILTER =================
// RPM increase speed
const float RPM_ATTACK = 0.18;
// RPM decrease speed
const float RPM_DECAY = 0.07;
// =====================================================
// IR SENSOR INTERRUPT
// =====================================================
void sensorPulse() {
unsigned long now = micros();
if (lastPulseMicros == 0) {
lastPulseMicros = now;
return;
}
unsigned long interval = now - lastPulseMicros;
// Noise filtering
if (interval >= MIN_PULSE_INTERVAL) {
pulseInterval = interval;
lastPulseMicros = now;
newPulse = true;
}
}
// =====================================================
// SETUP
// =====================================================
void setup() {
Serial.begin(115200);
pinMode(IR_PIN, INPUT_PULLUP);
rpmServo.attach(SERVO_PIN);
// Start position
rpmServo.write(0);
attachInterrupt(
digitalPinToInterrupt(IR_PIN),
sensorPulse,
FALLING
);
}
// =====================================================
// LOOP
// =====================================================
void loop() {
// ===================================================
// GET SENSOR DATA
// ===================================================
noInterrupts();
unsigned long interval = pulseInterval;
unsigned long lastPulse = lastPulseMicros;
bool pulse = newPulse;
newPulse = false;
interrupts();
// ===================================================
// CALCULATE RPM
// ===================================================
if (pulse && interval > 0) {
measuredRPM =
(60000000.0 / interval) / PULSES_PER_REV;
// Maximum 10,000 RPM
if (measuredRPM > MAX_RPM) {
measuredRPM = MAX_RPM;
}
// Remove tiny unwanted values
if (measuredRPM < 5) {
measuredRPM = 0;
}
}
// ===================================================
// NO SIGNAL DETECTION
// ===================================================
unsigned long currentMicros = micros();
if (lastPulse != 0) {
if ((currentMicros - lastPulse) > NO_PULSE_TIMEOUT) {
measuredRPM = 0;
}
}
// ===================================================
// PROFESSIONAL NEEDLE FILTER
// ===================================================
// RPM going UP
if (measuredRPM > filteredRPM) {
filteredRPM +=
(measuredRPM - filteredRPM) * RPM_ATTACK;
}
// RPM going DOWN
else {
filteredRPM +=
(measuredRPM - filteredRPM) * RPM_DECAY;
}
// Small values ko zero
if (filteredRPM < 1.0) {
filteredRPM = 0;
}
// ===================================================
// RPM -> SERVO ANGLE
// ===================================================
targetAngle =
(filteredRPM / MAX_RPM) * 180.0;
// Safety limits
if (targetAngle < 0) {
targetAngle = 0;
}
if (targetAngle > 180) {
targetAngle = 180;
}
// ===================================================
// SMOOTH SERVO NEEDLE
// ===================================================
if (millis() - lastServoUpdate >= SERVO_UPDATE) {
lastServoUpdate = millis();
// Needle UP
if (currentAngle < targetAngle) {
currentAngle += SERVO_STEP;
if (currentAngle > targetAngle) {
currentAngle = targetAngle;
}
}
// Needle DOWN
else if (currentAngle > targetAngle) {
currentAngle -= SERVO_STEP;
if (currentAngle < targetAngle) {
currentAngle = targetAngle;
}
}
// Servo position
rpmServo.write((int)currentAngle);
}
// ===================================================
// SERIAL MONITOR
// ===================================================
static unsigned long lastSerial = 0;
if (millis() - lastSerial >= 200) {
lastSerial = millis();
Serial.print("RPM: ");
Serial.print(filteredRPM, 0);
Serial.print(" | Angle: ");
Serial.println(currentAngle, 1);
}
}