To build a reliable arduino rev counter, you need to move past optical sensors that fail in dusty environments and use a digital Hall effect sensor paired with a hardware interrupt. By measuring the microsecond time delta between magnetic pulses, you can calculate RPM with sub-percent accuracy. This guide targets the Arduino Uno R4 Minima, leveraging its 48MHz Cortex-M4 processor to handle high-speed interrupt service routines (ISRs) without the timing jitter found in older 16MHz AVR boards.

Spec Sheet & Component Selection

The foundation of a stable rev counter is selecting a sensor with built-in hysteresis to prevent switch bounce. The A3144EUA-T is the industry standard for this. Below is the exact bill of materials (BOM) with 2026 market pricing and critical specifications.

Component Exact Model / Variant Key Specification Est. Cost
Microcontroller Arduino Uno R4 Minima 48MHz, 5V logic, hardware I2C $22.00
Hall Sensor Allegro A3144EUA-T Unipolar, 10kHz max freq, open-collector $1.80
Trigger Magnet N52 Neodymium 10x2mm South-pole face, ~3mm air gap limit $0.50
Display 0.96" I2C OLED (SSD1306) 128x64, 3.3V-5V tolerant, 0x3C addr $6.50
Pull-up Resistor 10kΩ 1/4W Metal Film Required for open-collector output $0.02

Reference: For a deep dive into Hall effect switch hysteresis and why unipolar sensors reject magnetic noise better than reed switches, see the Digikey Technical Article on Hall Effect Sensors.

Hardware Wiring & Pin Mapping

The A3144 features an open-collector output. This means it can pull the signal line to ground, but it cannot drive it high. If you wire this directly to an Arduino input without a pull-up resistor, the pin will float, and your serial monitor will spit out 60,000 RPM of electrical noise.

Pin Mapping Table

Component Pin Arduino Uno R4 Minima Pin Notes
A3144 VCC (Pin 1) 5V Do not use 3.3V; A3144 requires 4.5V min.
A3144 OUT (Pin 3) D2 (INT0) Must use Pin 2 or 3 for hardware interrupts.
A3144 GND (Pin 2) GND Common ground with Arduino and motor.
10kΩ Resistor Between 5V and D2 Pulls D2 HIGH when magnet is absent.
OLED SDA A4 Hardware I2C data line.
OLED SCL A5 Hardware I2C clock line.

Wiring Steps

  1. Mount the Magnet: Glue the N52 neodymium magnet to your rotating shaft. Ensure the South pole faces the sensor. (The A3144 is unipolar and will ignore the North pole entirely).
  2. Position the Sensor: Mount the A3144 within 2mm to 4mm of the magnet's path. Use a non-magnetic bracket (plastic or aluminum).
  3. Wire the Pull-up: Solder the 10kΩ resistor directly between the A3144 VCC and OUT pins, or use a breadboard to bridge 5V to D2.
  4. Connect I2C Display: Wire the SSD1306 OLED to A4/A5. If your module has a logic-level shifter, it's safe on the 5V bus.

Complete Compilable Code

The code below targets the Arduino Uno R4 Minima (and is backward-compatible with the Uno R3). It uses micros() inside the ISR for high-resolution timing and includes a timeout handler to drop the RPM to zero when the motor stalls, preventing the display from freezing on the last known speed.

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// --- PIN DEFINITIONS ---
const int HALL_PIN = 2; // Must be hardware interrupt pin (2 or 3 on Uno)

// --- DISPLAY SETUP ---
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

// --- VOLATILE ISR VARIABLES ---
volatile unsigned long lastPulseTime = 0;
volatile unsigned long pulseDelta = 0;
volatile bool newPulseReceived = false;

void hallISR() {
  unsigned long currentTime = micros();
  // Software debounce: ignore glitches faster than 100us (10,000 RPM limit per magnet)
  if (currentTime - lastPulseTime > 100) {
    pulseDelta = currentTime - lastPulseTime;
    lastPulseTime = currentTime;
    newPulseReceived = true;
  }
}

void setup() {
  Serial.begin(115200);
  
  // Initialize Hall Sensor Pin
  pinMode(HALL_PIN, INPUT); // External 10k pull-up used, internal not needed
  attachInterrupt(digitalPinToInterrupt(HALL_PIN), hallISR, FALLING);
  
  // Initialize OLED
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Halt if display fails
  }
  
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(2);
  display.setCursor(0, 20);
  display.println("READY");
  display.display();
  
  lastPulseTime = micros();
}

void loop() {
  float rpm = 0.0;
  
  // Check for stalled motor (timeout after 1 second)
  if (micros() - lastPulseTime > 1000000UL) {
    rpm = 0.0;
    if (newPulseReceived) {
      Serial.println("ERR: HALL_TIMEOUT - Check Magnet Polarity or Air Gap");
      newPulseReceived = false; // Prevent spamming serial
    }
  } 
  else if (newPulseReceived) {
    // Calculate RPM: 60,000,000 microseconds in a minute
    noInterrupts(); // Safely read 32-bit variable
    unsigned long delta = pulseDelta;
    interrupts();
    
    if (delta > 0) {
      rpm = 60000000.0 / (float)delta;
    }
    newPulseReceived = false;
  }
  
  // Update Display and Serial
  display.clearDisplay();
  display.setCursor(0, 10);
  display.setTextSize(1);
  display.println("MOTOR RPM");
  
  display.setTextSize(3);
  display.setCursor(0, 30);
  display.print((int)rpm);
  display.display();
  
  Serial.print("RPM: ");
  Serial.println(rpm, 1);
  
  delay(100); // Refresh rate limit for OLED
}

Note on Arduino Interrupts: For official documentation on how attachInterrupt() maps to hardware vectors across different architectures, refer to the Arduino Language Reference for External Interrupts.

Debugging: First Three Things to Check When It Fails

If your serial monitor outputs ERR: HALL_TIMEOUT - Check Magnet Polarity or Air Gap or the RPM reads a static zero while the shaft is spinning, do not rewrite your code. Hardware and magnetic physics are almost always the culprit. Run through these three checks in order:

1. Magnet Polarity (The Unipolar Trap)
The A3144 is a unipolar switch. It only triggers when the magnetic field exceeds the operate point (typically 30 Gauss) and releases when it drops below the release point (typically 15 Gauss). If you glued the magnet with the North pole facing the sensor, it will never trigger. Flip the magnet or swap to a bipolar latch sensor like the A3212.
2. Missing or Undersized Pull-Up Resistor
If your RPM reads erratically between 10,000 and 65,000 while the motor is off, your pin is floating. Verify the 10kΩ resistor is physically connected between 5V and Pin D2. Do not rely on the Arduino's internal INPUT_PULLUP (which is ~20kΩ-50kΩ) for high-speed noise rejection in industrial environments; an external 10kΩ or 4.7kΩ metal film resistor provides a stiffer pull-up and sharper rising edges.
3. Air Gap Exceeding Sensor Range
Magnetic field strength drops off at the cube of the distance. A 10x2mm N52 magnet might trigger the A3144 at 3mm, but if vibration pushes the air gap to 5mm, the field strength drops below the 30 Gauss threshold. Measure the physical gap with feeler gauges. If it exceeds 4mm, use a larger magnet or switch to a high-sensitivity gear-tooth sensor like the Allegro ATS616.

Extending and Simplifying the Build

Depending on your application, a single-magnet 1:1 resolution setup might be overkill or insufficient. Here is how to scale the design.

How to Simplify (Low-Cost / Low-RPM)

  • Drop the OLED: Remove the I2C display code and rely purely on Serial.println(). This frees up I2C pins and reduces code footprint for data-logging applications.
  • Use a Reed Switch: If your motor never exceeds 3,000 RPM, replace the A3144 with a standard glass reed switch (e.g., OKI ORD211). Reed switches are mechanically slower and suffer from contact bounce, but they require no power, no pull-up resistors, and trigger on both magnetic poles.

How to Extend (High-Resolution / Multi-Pole)

  • Multi-Magnet Arrays: To increase resolution at low speeds (e.g., a wind turbine anemometer), mount 4 magnets alternating North-South-North-South. Update the code math to divide the final RPM by the number of magnetic transitions per revolution: rpm = 60000000.0 / (delta * 4);.
  • Quadrature Encoders for Direction: A Hall effect switch cannot detect direction. If you need to know if the motor is running forward or reverse, replace the A3144 with a rotary encoder module (like the KY-040) and use two interrupt pins to read the phase shift between Channel A and Channel B.

By understanding the open-collector nature of your sensor and the microsecond-level timing of hardware interrupts, your arduino rev counter will deliver laboratory-grade telemetry on a hobbyist budget.