How to slow down the signal from a Hall effect sensor

Make a PLL with a Bresenham algorithm: add 4000 every received pulse, subtract 4500 and pulse whenever you reach 4500:

int bresenham = 0;
int dx = 4000;
int dy = 4500;

unsigned long out_interval = 20;
uint32_t last_pulse = 0;

void setup() {
  // put your setup code here, to run once:

  Serial.begin(115200);
  pinMode(LED_BUILTIN, OUTPUT);
}

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

  static unsigned long last = 0;
  // edit mistake:
  // 1/(4000 pulse/mile * 60 mi/hr /(3600s/hr*1000ms/s))) gives  
  uint32_t interval = 3600UL*1000/4000/60  ; // mph to ms/pulsein
  uint32_t now = millis();

  if (now - last >= interval ) { // simulate pulse in
    last = now;
    bresenham += dx;
    Serial.print('^');
  }
  if (bresenham > dy) {
    bresenham -= dy;
    digitalWrite(LED_BUILTIN, HIGH);
    Serial.print('_');
    last_pulse = now;
  }

  if (digitalRead(LED_BUILTIN) && now - last_pulse > out_interval)
  { // turn off outpulse
    digitalWrite(LED_BUILTIN, LOW);
  }
}

What kind of device is installed (original) in the car that processes the pulses?

Since my last code didn't actually have a PLL (or need a PLL), and because I thought it was interesting, I implemented one to even out the intervals between pulses. It also has the added benefit of handling the opposite case of speeding up a pulse train, such as 4500 pulses out per 4000 pulses in.

// This simulates a pulse train on simPin,
// detects the pulses on pinIn,
// runs a PLL at pll_div * the speed of the pulses,
// Runs a bresenham algorithm on the ratio of dy/dx for outpin pulses of out_interval us
// if dy is faster than dx, you should use a bigger pll_div than 1 to provide
// distinct intervals between double-steps and reduced jitter.
// if dy < dx, a pll_div of 1 is OK
//
// https://wokwi.com/projects/355885955385104385
// for https://forum.arduino.cc/t/how-to-slow-down-the-signal-from-a-hall-effect-sensor/1086525/3
//
// https://www.romanblack.com/one_sec.htm is good reading.
// Look at the sections on the sinewave inverter

int32_t bresenham = 0;
int32_t bres_pll = 0;
int dy = 4000;
int dx = 4500;
const int pll_div = 4;  //  how many sub-pulses per input pulse
const int debug = 2; // level of serial output

unsigned long out_interval = 1000;
uint32_t last_pulse = 0;

const int pinIn = 3;
const int simPin = pinIn;
const int outpin = LED_BUILTIN;



void setup() {
  // put your setup code here, to run once:

  Serial.begin(115200);
  pinMode(outpin, OUTPUT);
  pinMode(pinIn, INPUT_PULLUP);
  pinMode(simPin, OUTPUT); // same pin used to simulate input
}

uint32_t now = 0;

void simpulses(void) {  // read the slide potentiometer and simulate a variable pulse train
  int simState = digitalRead(simPin);
  static uint32_t lastSim = 0;
  static unsigned long last = 0;
  // 4000/mi / (X mi/hr 1hr/3600s * 1s/1000us)
  // 1/(4000 pulse/mile * 60 mi/hr /(3600s/hr*1000ms/s))) gives
  static uint32_t interval = 3600UL  * 1000 * 1000 / 4000 / 10 * 0 + 1000000UL  ; // mph to ms/pulsein
  static int32_t lastRead = -2500000UL;
  static int lastA0 = 0;

  if ( now - lastRead > 250000UL && abs((int)analogRead(A0) - lastA0) > 0 ) {
    lastA0 = analogRead(A0);
    //interval = 6000UL + 1000UL * lastA0;
    interval = 3600UL * 1e6 * 1024 / 4000 / (0 + lastA0) / 15;
    if (debug > 0) {
      Serial.print("mph:");
      Serial.print(interval ? 3600.0 * 1e6 / 4000 / interval : 0.0);
    }
    lastRead = now;
  }

  if (now - last >= interval ) { // simulate pulse
    last = now;
    digitalWrite(simPin, HIGH);
    lastSim = now;
    //Serial.print('^');
  }

  if (simState == HIGH && now - lastSim  >= out_interval) {
    // turn off simulated pulse
    digitalWrite(simPin, LOW);
  }
}


void loop() {
  int inState = digitalRead(pinIn);
  uint32_t lastInterval = 0;

  static int lastin = HIGH;
  static uint32_t lastFall = 0;

  static uint32_t dt = 1;
  static uint32_t lastpll = 0;

  int simState = digitalRead(simPin);
  static uint32_t lastSim = 0;
  static uint32_t pllSteps = 0;

  now = micros();
  simpulses();

  if (inState != lastin) { // check input pulse
    if ( inState == LOW) { //falling edge
      lastInterval = now - lastFall;

      bres_pll = lastInterval; // += sensitive to wide swings near zero
      dt = max(1, lastInterval / pll_div); //

      lastFall = now;
      if (debug > 0)Serial.print('\\'); // falling edge
      //Serial.print(bres_pll);
      //Serial.print(dt);
      //Serial.print(pllSteps);
    } else {
      if (debug > 2) Serial.print('/'); // rising edge

    }
    lastin = inState;
  }

  // Run PLL at input freq * pll_div
  if (now - lastpll >= dt && bres_pll +dt > 0) { 
    lastpll += dt;
    if (bres_pll > 0) { //
      bres_pll -= dt;
      bresenham += dy;
      if (debug > 1)Serial.print('.');
      if (debug > 3)   Serial.print(bresenham);
    }
  }

  if (now - last_pulse > 6000 && bresenham > (int32_t)dx * pll_div) { //
    bresenham -= (int32_t)dx * pll_div;
    digitalWrite(LED_BUILTIN, HIGH);
    if (debug > 0)Serial.print('*');
    if (debug > 3)Serial.print(bresenham);
    last_pulse = now;
  }

  if (digitalRead(LED_BUILTIN) && now - last_pulse > out_interval)
  { // turn off outpulse
    digitalWrite(LED_BUILTIN, LOW);
    if (debug > 2)   Serial.print('_');

  }
}

4000 pulses/mile at 150mph is 1/(4000p/mi*150mi/hr/(3600s/hr)=0.006s so there's lots of time to work with.

The cool thing about using the Bresenham algorithm is that by adding and subtracting integers, rather than dividing, you keep track of the fractional values and the phase.