// 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('_');

  }
}