Arduino Tachometer - getting needle float and not sure why

Hi all,

I've got quite far with this problem but I seem to have come up against a bit of a wall that I can't get past... The task is simple and uses two Teensy 3.2 Arduinos to test some code:

Arduino 1 (Transmitter) - Read a potentiometer and convert the input to a square wave pulse on one of the output pins between around 122 and 2445Hz to simulate 10-200mph as read by a gearbox 17 tooth wheel

Arduino 2 (Receiver) - To read the pulse rate and convert into a speed value that can be displayed using a stepper motor.

The code I'm using for the transmitter:

#include <AnalogSmooth.h>

/* 2126mm per tyre revolution = 470 revs per km
  diff ratio 3.42 so 1607 revs per km on gearbox = 1.6074 revs per m

  So 10mph = 4.4704 m/s = *3.42 diff ratio = 7.19 revs/s = 122.23 pulses /s (17 teeth on gearbox wheel)  = interval time of 8.181ms = 8181
  200mph = 89.408 m/s = 42.0545 revs/s *3.42 diff ratio = 143.826 GB revs/s = 2445.05 pulses /s = interval time of 0.4089ms = 409

*/

int sensorPin = A0;              // select the input pin for the potentiometer
unsigned long potReading = 0;            // variable to store the value coming from the sensor

unsigned long val = 0;                    //value to be used with map
const int speedled =  13;      // the number of the LED pin //speed pulse
const int speedpin =  12;      // the number of the pulse pin //speed pulse


int ledState = LOW;            // ledState used to set the LED
int ledState2 = LOW;           // ledState used to set the LED

unsigned long currentMicros = 0;
unsigned long previousMicros = 0;      // will store last time LED was updated
unsigned long interval = 500;          // interval at which to blink (milliseconds
unsigned long time = 0;
unsigned long previousTime = 0;      // will store last time Time was updated

unsigned long previousMillis = 0;      // will store last time LED was updated
unsigned long Minterval = 50;          // interval at which to blink (milliseconds
unsigned long Mtime = 0;


float damping_coefficient = 0.01;
float Hz = 0;
int i = 1;

//--------------------------------------
// Defaults to window size 10
AnalogSmooth as = AnalogSmooth();

// Window size can range from 1 - 100
AnalogSmooth as100 = AnalogSmooth(100);
//----------------------------------------
void setup()
{
  Serial.begin(115200);
  // Serial.println("Start...");
  // set the digital pin as output:
  pinMode(speedpin, OUTPUT);
  pinMode(speedled, OUTPUT);
}

void loop()
{

  potReading = as100.analogReadSmooth(sensorPin) / 10;
  potReading = potReading * potReading;
  potReading = potReading / 100;

  unsigned long currentMillis = millis();

  Mtime = currentMillis - previousMillis;
  if (Mtime > Minterval)
  {

    //Tone Version (weird response)
    //Hz = map(potReading, 104, 1, 122, 2445);        // interval adjuster from .40ms to 8.18ms //8196   1169   //10465, 100, 8181, 409
    //Serial.println(Hz);

    //Count Version
    //Serial.println(potReading);
    interval = map(potReading, 104, 1, 8181, 409);        // interval adjuster from .40ms to 8.18ms //8196   1169   //10465, 100, 8181, 409

    previousMillis = currentMillis;
    Hz = 1000000.0 / interval;
    Serial.println(Hz, 2);
    Serial.println(interval);

  }
  //Tone Version
  //tone(speedpin,Hz);


  //Count Version
  currentMicros = micros();
  time = currentMicros - previousMicros;
  if (time > interval)
  {
    ledState = !ledState;                      //  1 -> 0 -> 1 -> 0 etc
    digitalWrite(speedpin, ledState);
    previousMicros = currentMicros;

    //digitalWrite(speedled, ledState);

  }
}

The code I'm using for the receiver:

#include <AccelStepper.h>

// Define some steppers and the pins the will use
AccelStepper stepper1(AccelStepper::DRIVER, 5, 6);
const int RESET = 18; // pin for RESET

//------------------------------------------
volatile unsigned long VSS_count = 0;
unsigned long VSS_prior_count = 0;
int pin_VSS = 14;

void pulse() {
  VSS_count = VSS_count + 1;
}

int SPEED_MPH;
int SPEED_STEPS;
int SPEED_SEND;
int currentMillis;
unsigned long lastMillis;
unsigned long VSS_new_count;
int teeth = 17;
float diffRatio = 3.42;
int WheelCirc = 2126; //mm
float toothDistance = (1.0 / (teeth));
float disttravelled = WheelCirc / diffRatio;
unsigned long elapsedTime;
unsigned long speedRaw;
int pulsegroup = 0;
unsigned long time;
//--------------------------------------------------

void setup()
{
  pinMode(RESET, OUTPUT);
  digitalWrite(RESET, LOW);
  delay(1);  // keep reset low min 1ms
  digitalWrite(RESET, HIGH);

  stepper1.setMaxSpeed(10000.0);
  stepper1.setAcceleration(50000.0);
  stepper1.runToNewPosition(3780);
  stepper1.runToNewPosition(0);

  //---------------------------------------------------
  toothDistance = toothDistance * disttravelled;
  //---Speed Input Count-----------------------------------
  pinMode(pin_VSS, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(pin_VSS), pulse, RISING);
  NVIC_SET_PRIORITY(IRQ_PORTA, 0);
  //---------------------------------------------------

}

void loop() {
  noInterrupts ();
  VSS_new_count = VSS_count;
  interrupts ();
  time = millis();

  pulsegroup = VSS_new_count - VSS_prior_count;
  if (pulsegroup > 9) {
    elapsedTime = (time - lastMillis);
    speedRaw = ((VSS_new_count - VSS_prior_count) * 60 * 1000 / (toothDistance * elapsedTime)) / 10;

    VSS_prior_count = VSS_new_count;

    lastMillis = time;
  }

  SPEED_STEPS = map(speedRaw, 10, 200, 0, 3780);
  stepper1.moveTo(SPEED_STEPS);

  stepper1.run();
}

Here's a video using the current setup on the transmitter (counting micros and changing pin state): VIDEO

I have also tried the other code currently deactivated where I use the tone() function and that seems to produce a more stable pointer, but it is less responsive - it sits still, then jumps quickly to the next position around 30-50 degrees away but the pot is being rotated smoothly.

So I have two problems with this that I'm not sure how to solve:

Problem 1) The pointer obviously isn't following the pot as the pot is linear and the change in frequency required is exponential. I have tried to square the pot input which has helped but the pointer still responds to low speed in much higher resolution than high speed so something still isn't set right.

Problem 2) Either the pulses aren't being generated uniformly, or the receiver isn't interpreting them properly. I don't have an oscilloscope so I can't check which side is causing the issue but I suspect it's the transmitter.

Instead of

previousMicros = currentMicros;

try

previousMicros += interval;

Hmm that didn't work but I tried deactivating the pot mapping line and fixing the interval at 1500 and a couple of other values and the needle actually still wobbles so perhaps it's actually the receiver...?

[EDIT] if I play with this line

if (pulsegroup > 30) {

then the wobble changes so I think the error is in here:

pulsegroup = VSS_new_count - VSS_prior_count;
if (pulsegroup > 30) {
elapsedTime = (time - lastMillis);
speedRaw = ((VSS_new_count - VSS_prior_count) * 60 * 1000 / (toothDistance * elapsedTime)) / 10;

VSS_prior_count = VSS_new_count;

lastMillis = time;
}

If you don't have access to a scope, the square wave frequencies are in the audio range, so you could try attaching a piezo sounder to the pin and listen to how the frequency changes as you move the pot.

Would it be worth experimenting with pulseIn() to measure the frequency, instead of counting pulses over a longer period?

I just thought the same thing and tried pulsein with the following code but the stepper motor response slowed to a snails pace - not sure why that would be

   ontime = pulseIn(pin_VSS,HIGH);
   offtime = pulseIn(pin_VSS,LOW);
   period = ontime+offtime;
   freq = 1000000.0/period;
   SPEED_STEPS = map(freq, 122, 2445, 0, 3780);